Class: AIPP::Parser

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Debugger
Defined in:
lib/aipp/parser.rb

Overview

AIP parser infrastructure

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options:) ⇒ Parser

Returns a new instance of Parser.


27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/aipp/parser.rb', line 27

def initialize(options:)
  @options = options
  @options[:storage] = options[:storage].join(options[:region])
  @options[:storage].mkpath
  @config = {}
  @aixm = AIXM.document(effective_at: @options[:airac].date)
  @dependencies = THash.new
  @fixtures = {}
  @borders = {}
  @cache = OpenStruct.new
  AIXM.send("#{options[:schema]}!")
  AIXM.config.region = options[:region]
end

Instance Attribute Details

#aixmAIXM::Document (readonly)

Returns target document.

Returns:

  • (AIXM::Document)

    target document


16
17
18
# File 'lib/aipp/parser.rb', line 16

def aixm
  @aixm
end

#bordersHash (readonly)

Returns map from border names to border objects.

Returns:

  • (Hash)

    map from border names to border objects


22
23
24
# File 'lib/aipp/parser.rb', line 22

def borders
  @borders
end

#cacheOpenStruct (readonly)

Returns object cache.

Returns:

  • (OpenStruct)

    object cache


25
26
27
# File 'lib/aipp/parser.rb', line 25

def cache
  @cache
end

#configHash (readonly)

Returns configuration read from config.yml.

Returns:

  • (Hash)

    configuration read from config.yml


13
14
15
# File 'lib/aipp/parser.rb', line 13

def config
  @config
end

#fixturesHash (readonly)

Returns map from AIP name to fixtures.

Returns:

  • (Hash)

    map from AIP name to fixtures


19
20
21
# File 'lib/aipp/parser.rb', line 19

def fixtures
  @fixtures
end

#optionsHash (readonly)

Returns passed command line arguments.

Returns:

  • (Hash)

    passed command line arguments


10
11
12
# File 'lib/aipp/parser.rb', line 10

def options
  @options
end

Instance Method Details

#inspectString

Returns:


42
43
44
# File 'lib/aipp/parser.rb', line 42

def inspect
  "#<AIPP::Parser>"
end

#parse_aipObject

Parse AIP by invoking the parser classes for the current region.


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/aipp/parser.rb', line 86

def parse_aip
  info("AIRAC #{options[:airac].id} effective #{options[:airac].date}", color: :green)
  AIPP::Downloader.new(storage: options[:storage], source: options[:airac].date.xmlschema) do |downloader|
    @dependencies.tsort(options[:aip]).each do |aip|
      info("parsing #{aip}")
      ("AIPP::%s::%s" % [options[:region], aip.remove(/\W/).camelcase]).constantize.new(
        aip: aip,
        downloader: downloader,
        fixture: @fixtures[aip],
        parser: self
      ).attach_patches.tap(&:parse).detach_patches
    end
  end
  if options[:grouped_obstacles]
    info("grouping obstacles")
    aixm.group_obstacles!
  end
  info("counting #{aixm.features.count} features")
end

#read_configObject

Read the configuration from config.yml.


47
48
49
50
51
52
# File 'lib/aipp/parser.rb', line 47

def read_config
  info("reading config.yml")
  @config = YAML.load_file(config_file, symbolize_names: true, fallback: {}) if config_file.exist?
  @config[:namespace] ||= SecureRandom.uuid
  @aixm.namespace = @config[:namespace]
end

#read_regionObject

Read the region directory and build the dependency list.


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/aipp/parser.rb', line 55

def read_region
  info("reading region #{options[:region]}")
  dir = Pathname(__FILE__).dirname.join('regions', options[:region])
  fail("unknown region `#{options[:region]}'") unless dir.exist?
  # Fixtures
  dir.glob('fixtures/*.yml').each do |file|
    verbose_info "reading fixture fixtures/#{file.basename}"
    fixture = YAML.load_file(file)
    @fixtures[file.basename('.yml').to_s] = fixture
  end
  # Borders
  dir.glob('borders/*.geojson').each do |file|
    verbose_info "reading border borders/#{file.basename}"
    border = AIPP::Border.from_file(file)
    @borders[file.basename] = border
  end
  # Helpers
  dir.glob('helpers/*.rb').each do |file|
    verbose_info "reading helper helpers/#{file.basename}"
    require file
  end
  # Parsers
  dir.glob('*.rb').each do |file|
    verbose_info "requiring #{file.basename}"
    require file
    aip = file.basename('.*').to_s
    @dependencies[aip] = ("AIPP::%s::%s::DEPENDS" % [options[:region], aip.remove(/\W/).camelcase]).constantize
  end
end

#validate_aixmObject

Validate the AIXM document.

Raises:

  • (RuntimeError)

    if the document is not valid


109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/aipp/parser.rb', line 109

def validate_aixm
  info("detecting duplicates")
  if (duplicates = aixm.features.duplicates).any?
    message = "duplicates found:\n" + duplicates.map { "#{_1.inspect} from #{_1.source}" }.join("\n")
    @options[:force] ? warn(message) : fail(message)
  end
  info("validating #{options[:schema].upcase}")
  unless aixm.valid?
    message = "invalid #{options[:schema].upcase} document:\n" + aixm.errors.map(&:message).join("\n")
    @options[:force] ? warn(message) : fail(message)
  end
end

#write_aixmObject

Write the AIXM document.


169
170
171
172
173
# File 'lib/aipp/parser.rb', line 169

def write_aixm
  info("writing #{aixm_file}")
  AIXM.config.mid = options[:mid]
  File.write(aixm_file, aixm.to_xml)
end

#write_buildObject

Write the AIXM document and context information.


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/aipp/parser.rb', line 123

def write_build
  if @options[:aip]
    info ("skipping build")
  else
    info("writing build")
    builds_path.mkpath
    build_file = builds_path.join("#{@options[:airac].date.xmlschema}.zip")
    Dir.mktmpdir do |tmp_dir|
      tmp_dir = Pathname(tmp_dir)
      # AIXM/OFMX file
      AIXM.config.mid = true
      File.write(tmp_dir.join(aixm_file), aixm.to_xml)
      # Build details
      File.write(
        tmp_dir.join('build.yaml'), {
          version: AIPP::VERSION,
          config: @config,
          options: @options
        }.to_yaml
      )
      # Manifest
      manifest = ['AIP','Feature', 'Comment', 'Short Uid Hash', 'Short Feature Hash'].to_csv
      manifest += aixm.features.map do |feature|
        xml = feature.to_xml
        element = xml.first_match(/<(\w{3})\s/)
        [
          feature.source.split('|')[2],
          element,
          xml.match(/<!-- (.*?) -->/)[1],
          AIXM::PayloadHash.new(xml.match(%r(<#{element}Uid\s.*?</#{element}Uid>)m).to_s).to_uuid[0,8],
          AIXM::PayloadHash.new(xml).to_uuid[0,8]
        ].to_csv
      end.sort.join
      File.write(tmp_dir.join('manifest.csv'), manifest)
      # Zip it
      build_file.delete if build_file.exist?
      Zip::File.open(build_file, Zip::File::CREATE) do |zip|
        tmp_dir.children.each do |entry|
          zip.add(entry.basename.to_s, entry) unless entry.basename.to_s[0] == '.'
        end
      end
    end
  end
end

#write_configObject

Write the configuration to config.yml.


176
177
178
179
# File 'lib/aipp/parser.rb', line 176

def write_config
  info("writing config.yml")
  File.write(config_file, config.to_yaml)
end