Class: AIPP::Parser

Inherits:
Object show all
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.


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

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


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

def aixm
  @aixm
end

#bordersHash (readonly)

Returns map from border names to border objects.

Returns:

  • (Hash)

    map from border names to border objects


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

def borders
  @borders
end

#cacheOpenStruct (readonly)

Returns object cache.

Returns:

  • (OpenStruct)

    object cache


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

def cache
  @cache
end

#configHash (readonly)

Returns configuration read from config.yml.

Returns:

  • (Hash)

    configuration read from config.yml


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

def config
  @config
end

#fixturesHash (readonly)

Returns map from AIP name to fixtures.

Returns:

  • (Hash)

    map from AIP name to fixtures


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

def fixtures
  @fixtures
end

#optionsHash (readonly)

Returns passed command line arguments.

Returns:

  • (Hash)

    passed command line arguments


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

def options
  @options
end

Instance Method Details

#parse_aipObject

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


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/aipp/parser.rb', line 80

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/).classify]).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.


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

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

#read_regionObject

Read the region directory and build the dependency list.


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/aipp/parser.rb', line 49

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.new(file)
    @borders[border.name] = 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/).classify]).constantize
  end
end

#validate_aixmObject

Validate the AIXM document.

Raises:

  • (RuntimeError)

    if the document is not valid


103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/aipp/parser.rb', line 103

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, pry: binding) : 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, pry: binding) : fail(message)
  end
end

#write_aixmObject

Write the AIXM document.


164
165
166
167
168
# File 'lib/aipp/parser.rb', line 164

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.


117
118
119
120
121
122
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
# File 'lib/aipp/parser.rb', line 117

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, buffer, feature, aip, uid, comment = [], '', '', '', '', ''
      File.open(tmp_dir.join(aixm_file)).each do |line|
        buffer << line
        case line
        when /^ {2}<(\w{3}).*source=".*?\|.*?\|(.*?)\|/ then buffer, feature, aip = line, $1, $2
        when /^ {4}<#{feature}Uid[^>]+?mid="(.*?)"/ then uid = $1
        when /^ {2}<!-- (.*) -->/ then comment = $1
        when /^ {2}<\/#{feature}>/
          manifest << [aip, feature, uid[0,8], AIXM::PayloadHash.new(buffer).to_uuid[0,8], comment].to_csv
          feature, aip, uid = '', '', ''
        end
      end
      manifest = manifest.sort.prepend "AIP,Feature,Short Uid Hash,Short Feature Hash,Comment\n"
      File.write(tmp_dir.join('manifest.csv'), manifest.join)
      # 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.


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

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