aipp 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +8 -0
  5. data/README.md +40 -14
  6. data/exe/aip2aixm +1 -1
  7. data/exe/aip2ofmx +1 -1
  8. data/lib/aipp.rb +2 -0
  9. data/lib/aipp/aip.rb +17 -12
  10. data/lib/aipp/downloader.rb +1 -1
  11. data/lib/aipp/executable.rb +15 -12
  12. data/lib/aipp/parser.rb +58 -43
  13. data/lib/aipp/pdf.rb +1 -1
  14. data/lib/aipp/regions/LF/AD-1.3.rb +7 -6
  15. data/lib/aipp/regions/LF/AD-1.6.rb +7 -5
  16. data/lib/aipp/regions/LF/AD-2.rb +16 -9
  17. data/lib/aipp/regions/LF/AD-3.1.rb +6 -6
  18. data/lib/aipp/regions/LF/ENR-2.1.rb +81 -6
  19. data/lib/aipp/regions/LF/ENR-4.1.rb +3 -1
  20. data/lib/aipp/regions/LF/ENR-4.3.rb +2 -3
  21. data/lib/aipp/regions/LF/ENR-5.1.rb +15 -2
  22. data/lib/aipp/regions/LF/ENR-5.4.rb +90 -0
  23. data/lib/aipp/regions/LF/ENR-5.5.rb +12 -10
  24. data/lib/aipp/regions/LF/fixtures/AD-1.3.yml +2 -2
  25. data/lib/aipp/regions/LF/helpers/base.rb +17 -9
  26. data/lib/aipp/regions/LF/helpers/radio_AD.rb +21 -13
  27. data/lib/aipp/t_hash.rb +3 -3
  28. data/lib/aipp/version.rb +1 -1
  29. data/lib/core_ext/enumerable.rb +7 -7
  30. data/lib/core_ext/string.rb +9 -4
  31. metadata +156 -168
  32. metadata.gz.sig +1 -0
  33. data/.github/workflows/test.yml +0 -26
  34. data/.gitignore +0 -8
  35. data/.ruby-version +0 -1
  36. data/.yardopts +0 -3
  37. data/Guardfile +0 -7
  38. data/TODO.md +0 -6
  39. data/aipp.gemspec +0 -45
  40. data/gems.rb +0 -3
  41. data/rakefile.rb +0 -12
  42. data/spec/fixtures/border.geojson +0 -201
  43. data/spec/fixtures/document.pdf +0 -0
  44. data/spec/fixtures/document.pdf.json +0 -1
  45. data/spec/fixtures/new.html +0 -6
  46. data/spec/fixtures/new.pdf +0 -0
  47. data/spec/fixtures/new.txt +0 -1
  48. data/spec/fixtures/source.zip +0 -0
  49. data/spec/lib/aipp/airac_spec.rb +0 -98
  50. data/spec/lib/aipp/border_spec.rb +0 -135
  51. data/spec/lib/aipp/downloader_spec.rb +0 -81
  52. data/spec/lib/aipp/patcher_spec.rb +0 -46
  53. data/spec/lib/aipp/pdf_spec.rb +0 -124
  54. data/spec/lib/aipp/t_hash_spec.rb +0 -44
  55. data/spec/lib/aipp/version_spec.rb +0 -7
  56. data/spec/lib/core_ext/enumberable_spec.rb +0 -76
  57. data/spec/lib/core_ext/hash_spec.rb +0 -27
  58. data/spec/lib/core_ext/integer_spec.rb +0 -15
  59. data/spec/lib/core_ext/nil_class_spec.rb +0 -11
  60. data/spec/lib/core_ext/string_spec.rb +0 -112
  61. data/spec/sounds/failure.mp3 +0 -0
  62. data/spec/sounds/success.mp3 +0 -0
  63. data/spec/spec_helper.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cddca26d9635de7aa2ded8bde0def36adf97d548e9c133358443f7812c778ef6
4
- data.tar.gz: cadd71e6709c70f03a6f2ceda4af1ef38b049d70f3aaad2cd45ef3fc792df192
3
+ metadata.gz: 11e3df230b0b8dcafdef31e97ed97e2bbf7d6c45f75cb86949ee92a3980e07f4
4
+ data.tar.gz: 9fe425721151ec0c99677813ac283de61f274f97a6d103cbce8d4d178e8f794d
5
5
  SHA512:
6
- metadata.gz: 5d511918c12c5c877c64cb4d5f28e5d968cdd98e08f9dfc72b8f188579da214aa63e953b36a5f17dbdd8b2d5d0281209f29d2693a458c4b7613d0d012c4111e7
7
- data.tar.gz: b3b4a1dc343049c3aec1c58290e52d09fb43adb476a6c085271d31f0d20c016db19cb9ed0f4355e8eb78f199fa3e6b9580a2f2a30d2e6ff8bcc6548f818d8e08
6
+ metadata.gz: 000f611816b7d7b7112649830b3cd1ae383fd9b2a6defcc554715bfbabb9c3008c1f90f3f8062d3c43630c06f7642cd8e8f3c559a99e6bb7ccc4e045062333b5
7
+ data.tar.gz: c29e73e68318fe197e06d07a08c79fd5cfcb6be9aff3c5fbb96150f0385682e5a51284caf311026b8623d342a77b73485db1509a844de3217161656b52e8818c
Binary file
Binary file
@@ -1,3 +1,11 @@
1
+ ## 0.2.6
2
+
3
+ #### Additions
4
+ * Detect duplicate features
5
+
6
+ #### Changes
7
+ * Require Ruby 2.7
8
+
1
9
  ## 0.2.5
2
10
 
3
11
  #### Additions
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  [![Version](https://img.shields.io/gem/v/aipp.svg?style=flat)](https://rubygems.org/gems/aipp)
2
- [![Tests](https://github.com/svoop/aipp/workflows/Test/badge.svg)](https://github.com/svoop/aipp/actions?workflow=Test)
2
+ [![Tests](https://img.shields.io/github/workflow/status/svoop/aipp/Test.svg?style=flat&label=tests)](https://github.com/svoop/aipp/actions?workflow=Test)
3
3
  [![Donorbox](https://img.shields.io/badge/donate-on_donorbox-yellow.svg)](https://donorbox.org/bitcetera)
4
4
 
5
5
  # AIPP
@@ -25,12 +25,36 @@ This gem incluces two executables to download and parse aeronautical data as HTM
25
25
 
26
26
  ## Install
27
27
 
28
- Add this to your <tt>Gemfile</tt> or <tt>gems.rb</tt>:
28
+ ### Security
29
+
30
+ This gem is [cryptographically signed](https://guides.rubygems.org/security/#using-gems) in order to assure it hasn't been tampered with. Unless already done, please add the author's public key as a trusted certificate now:
31
+
32
+ ```
33
+ gem cert --add <(curl -Ls https://raw.github.com/svoop/aipp/master/certs/svoop.pem)
34
+ ```
35
+
36
+ ### Standalone
37
+
38
+ Make sure to have the [latest version of Ruby](https://www.ruby-lang.org/en/documentation/installation/) and then install this gem:
39
+
40
+ ```
41
+ gem install aipp --trust-policy MediumSecurity
42
+ ```
43
+
44
+ ### Bundler
45
+
46
+ If you're familiar with [Bundler](https://bundler.io) powered Ruby projects, you might prefer to add the following to your <tt>Gemfile</tt> or <tt>gems.rb</tt>:
29
47
 
30
48
  ```ruby
31
49
  gem aipp
32
50
  ```
33
51
 
52
+ And then install the bundle:
53
+
54
+ ```
55
+ bundle install --trust-policy MediumSecurity
56
+ ```
57
+
34
58
  ## Usage
35
59
 
36
60
  See the built-in help for all options:
@@ -61,23 +85,23 @@ You'll find a directory for each region which contains the following items:
61
85
  * `manifest.csv` – diffable manifest (see below)
62
86
  * `config.yml`<br>This file contains configuration which will be read on subsequent runs, most notably the namespace UUID used to identify the creator of OFMX files.
63
87
 
64
- The manifest is a CSV which lists every feature on a separate line along with its hashes and comment. You can `diff` or `git diff` two manifests:
88
+ The manifest is a CSV which lists every feature on a separate line along with its hashes, AIP and comment. You can `diff` or `git diff` two manifests:
65
89
 
66
90
  ```diff
67
91
  $ git diff -U0 2019-09-12/manifest.csv 2019-10-10/manifest.csv
68
92
 
69
93
  --- a/2019-09-12/manifest.csv
70
94
  +++ b/2019-10-10/manifest.csv
71
- @@ -89,0 +90 @@ Aha,449f4791,d6d4bff8,Address: PHONE for LFF958FC61
72
- +Aha,44b9a044,cf55a3cf,Address: PHONE for LF49929DD6
73
- @@ -134 +135 @@ Aha,6acfbcc5,c4f57e3f,Address: FAX for LF897E1A6F
74
- -Aha,6b381b32,fb947716,Address: RADIO for LFPO
75
- +Aha,6b381b32,b9723b7e,Address: RADIO for LFPO
76
- @@ -253,0 +255 @@ Aha,d01c4282,7cc8db7f,Address: PHONE for LF0C413AE2
77
- +Aha,d05cfbff,74fb76bc,Address: FAX for LF49929DD6
78
- @@ -327 +329 @@ Ahp,068f4f01,1dc2fb5c,Airport: LFEA BELLE ILE
79
- -Ahp,06d04910,e18af958,Airport: LFFDE9077D TONNERRE CENTRE HOSPITALIER
80
- +Ahp,06d04910,cc522711,Airport: LFFDE9077D TONNERRE CENTRE HOSPITALIER
95
+ @@ -204 +204 @@ AD-1.3,Ahp,9e9f031e,d6f22057,Airport: LFLJ COURCHEVEL
96
+ -AD-1.3,Ahp,9f1eed18,37ddbbde,Airport: LFQD ARRAS ROCLINCOURT
97
+ +AD-1.3,Ahp,9f1eed18,f0e60105,Airport: LFQD ARRAS ROCLINCOURT
98
+ @@ -312 +312 @@ AD-2,Aha,4250c9ee,04d49dc7,Address: RADIO for LFHV
99
+ -AD-2,Aha,6b381b32,fb947716,Address: RADIO for LFPO
100
+ +AD-2,Aha,6b381b32,b9723b7e,Address: RADIO for LFPO
101
+ @@ -664 +663,0 @@ AD-2,Ser,3920a7fd,4545c5eb,Service: AFIS by LFGA TWR
102
+ -AD-2,Ser,39215774,1f13f2cf,Service: APP by LFCR APP
103
+ @@ -878 +876,0 @@ AD-2,Ser,bb5228d7,7cfb4572,Service: TWR by LFMH TWR
104
+ -AD-2,Ser,bc72caf2,0a15b39c,Service: FIS by LFCR FIC
81
105
  (...)
82
106
  ```
83
107
 
@@ -135,7 +159,9 @@ Inside the `parse` method, you have access to the following methods:
135
159
 
136
160
  * [`read`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#read-instance_method) – download and read an AIP file
137
161
  * [`add`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#add-instance_method) – add a [`AIXM::Feature`](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
138
- * [`select`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find-instance_method) – search previously written [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
162
+ * [`find`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find-instance_method) – find previously written [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature) by object
163
+ * [`find_by`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find_by-instance_method) – find previously written [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature) by class and attribute values
164
+ * [`unique`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#unique-instance_method) – prevent duplicate [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
139
165
  * some core extensions from ActiveSupport – [`Object#blank`](https://www.rubydoc.info/gems/activesupport/Object#blank%3F-instance_method) and [`String`](https://www.rubydoc.info/gems/activesupport/String)
140
166
  * core extensions from this gem – [`Object`](https://www.rubydoc.info/gems/aipp/Object), [`NilClass`](https://www.rubydoc.info/gems/aipp/NilClass), [`Integer`](https://www.rubydoc.info/gems/aipp/Integer), [`String`](https://www.rubydoc.info/gems/aipp/String), [`Hash`](https://www.rubydoc.info/gems/aipp/Hash) and [`Enumerable`](https://www.rubydoc.info/gems/aipp/Enumerable)
141
167
 
@@ -4,7 +4,7 @@ require 'bundler/inline'
4
4
 
5
5
  gemfile do
6
6
  source 'https://rubygems.org'
7
- ruby '>= 2.5'
7
+ ruby '>= 2.7'
8
8
  gem 'aipp', '~> 0'
9
9
  end
10
10
 
@@ -4,7 +4,7 @@ require 'bundler/inline'
4
4
 
5
5
  gemfile do
6
6
  source 'https://rubygems.org'
7
- ruby '>= 2.5'
7
+ ruby '>= 2.7'
8
8
  gem 'aipp', '~> 0'
9
9
  end
10
10
 
@@ -41,6 +41,8 @@ require_relative 'aipp/parser'
41
41
  require_relative 'aipp/downloader'
42
42
 
43
43
  # Disable "did you mean?" suggestions
44
+ #
45
+ # @!visibility private
44
46
  module DidYouMean::Correctable
45
47
  remove_method :to_s
46
48
  end
@@ -52,24 +52,29 @@ module AIPP
52
52
  # Add feature to AIXM
53
53
  #
54
54
  # @param feature [AIXM::Feature] e.g. airport or airspace
55
+ # @return [AIXM::Feature] added feature
55
56
  def add(feature)
56
57
  verbose_info "Adding #{feature.inspect}"
57
- aixm.features << feature
58
+ aixm.add_feature feature
59
+ feature
58
60
  end
59
61
 
60
- # Search features previously written to AIXM and return those matching the
61
- # given class and attribute values
62
+ # @!method find_by(klass, attributes={})
63
+ # Find objects of the given class and optionally with the given attribute
64
+ # values previously written to AIXM.
62
65
  #
63
- # @example
64
- # select(:airport, id: "LFNT")
66
+ # @note This method is delegated to +AIXM::Association::Array+.
67
+ # @see https://www.rubydoc.info/gems/aixm/AIXM/Association/Array#find_by-instance_method
65
68
  #
66
- # @param klass [Class, Symbol] feature class like AIXM::Feature::Airport or
67
- # AIXM::Feature::NavigationalAid::VOR, shorthand notations as symbols
68
- # e.g. :airport or :vor as listed in AIXM::CLASSES are recognized as well
69
- # @param attributes [Hash] filter by these attributes and their values
70
- # @return [Array<AIXM::Feature>]
71
- def select(*args)
72
- aixm.select_features(*args)
69
+ # @!method find(object)
70
+ # Find equal objects previously written to AIXM.
71
+ #
72
+ # @note This method is delegated to +AIXM::Association::Array+.
73
+ # @see https://www.rubydoc.info/gems/aixm/AIXM/Association/Array#find-instance_method
74
+ %i(find_by find).each do |method|
75
+ define_method method do |*args|
76
+ aixm.features.send(method, *args)
77
+ end
73
78
  end
74
79
  end
75
80
 
@@ -58,7 +58,7 @@ module AIPP
58
58
  file = work_path.join([document, type].join('.'))
59
59
  unless file.exist?
60
60
  verbose_info "Downloading #{document}"
61
- IO.copy_stream(Kernel.open(url), file)
61
+ IO.copy_stream(URI.open(url), file)
62
62
  end
63
63
  convert file
64
64
  end
@@ -15,15 +15,18 @@ module AIPP
15
15
  Download online AIP and convert it to #{options[:schema].upcase}.
16
16
  Usage: #{File.basename($0)} [options]
17
17
  END
18
- o.on('-d', '--airac DATE', String, %Q[AIRAC date (default: "#{@options[:airac].date.xmlschema}")]) { |v| @options[:airac] = AIPP::AIRAC.new(v) }
19
- o.on('-r', '--region STRING', String, 'region (e.g. "LF")') { |v| @options[:region] = v.upcase }
20
- o.on('-a', '--aip STRING', String, 'process this AIP only (e.g. "ENR-5.1")') { |v| @options[:aip] = v.upcase }
21
- o.on('-m', '--[no-]mid', 'insert mid attributes into all Uid elements (default: false)') { |v| @options[:mid] = v }
22
- o.on('-s', '--storage DIR', String, 'storage directory (default: "~/.aipp")') { |v| @options[:storage] = Pathname(v) }
23
- o.on('-f', '--[no-]force', 'ignore XML schema validation (default: false)') { |v| @options[:force] = v }
24
- o.on('-v', '--[no-]verbose', 'verbose output (default: false)') { |v| $VERBOSE_INFO = v }
25
- o.on('-w', '--pry-on-warn [ID]', Integer, 'open pry on warn with ID (default: nil)') { |v| $PRY_ON_WARN = v || true }
26
- o.on('-e', '--[no-]pry-on-error', 'open pry on error (default: false)') { |v| $PRY_ON_ERROR = v }
18
+ o.on('-d', '--airac DATE', String, %Q[AIRAC date (default: "#{@options[:airac].date.xmlschema}")]) { @options[:airac] = AIPP::AIRAC.new(_1) }
19
+ o.on('-r', '--region STRING', String, 'region (e.g. "LF")') { @options[:region] = _1.upcase }
20
+ o.on('-a', '--aip STRING', String, 'process this AIP only (e.g. "ENR-5.1")') { @options[:aip] = _1.upcase }
21
+ if options[:schema] == :ofmx
22
+ o.on('-g', '--[no-]grouped-obstacles', 'group obstacles (time-consuming!)') { @options[:grouped_obstacles] = _1 }
23
+ o.on('-m', '--[no-]mid', 'insert mid attributes into all Uid elements (default: false)') { @options[:mid] = _1 }
24
+ end
25
+ o.on('-s', '--storage DIR', String, 'storage directory (default: "~/.aipp")') { @options[:storage] = Pathname(_1) }
26
+ o.on('-f', '--[no-]force', 'ignore XML schema validation (default: false)') { @options[:force] = _1 }
27
+ o.on('-v', '--[no-]verbose', 'verbose output (default: false)') { $VERBOSE_INFO = _1 }
28
+ o.on('-w', '--pry-on-warn [ID]', Integer, 'open pry on warn with ID (default: nil)') { $PRY_ON_WARN = _1 || true }
29
+ o.on('-e', '--[no-]pry-on-error', 'open pry on error (default: false)') { $PRY_ON_ERROR = _1 }
27
30
  o.on('-A', '--about', 'show author/license information and exit') { about }
28
31
  o.on('-R', '--readme', 'show README and exit') { readme }
29
32
  o.on('-L', '--list', 'list implemented regions and AIPs') { list }
@@ -42,12 +45,12 @@ module AIPP
42
45
  parser.read_region
43
46
  parser.parse_aip
44
47
  parser.validate_aixm
45
- parser.write_aixm
46
48
  parser.write_build
49
+ parser.write_aixm
47
50
  parser.write_config
48
51
  end
49
52
  rescue => error
50
- puts "ERROR: #{error.message}"
53
+ puts "ERROR: #{error.message}".magenta
51
54
  Pry::rescued(error) if $PRY_ON_ERROR
52
55
  end
53
56
  end
@@ -69,7 +72,7 @@ module AIPP
69
72
  regions_path = Pathname($0).dirname.join('..', 'gems', "aipp-#{AIPP::VERSION}", 'lib', 'aipp', 'regions')
70
73
  hash = Dir.each_child(regions_path).each.with_object({}) do |region, hash|
71
74
  hash[region] = Dir.children(regions_path.join(region)).sort.map do |aip|
72
- File.basename(aip, '.rb') unless aip == 'helper.rb'
75
+ File.basename(aip, '.rb') if File.file?(regions_path.join(region, aip))
73
76
  end.compact
74
77
  end
75
78
  puts hash.to_yaml.sub(/\A\W*/, '')
@@ -28,12 +28,13 @@ module AIPP
28
28
  @options[:storage] = options[:storage].join(options[:region])
29
29
  @options[:storage].mkpath
30
30
  @config = {}
31
- @aixm = AIXM.document(region: @options[:region], effective_at: @options[:airac].date)
31
+ @aixm = AIXM.document(effective_at: @options[:airac].date)
32
32
  @dependencies = THash.new
33
33
  @fixtures = {}
34
34
  @borders = {}
35
35
  @cache = OpenStruct.new
36
36
  AIXM.send("#{options[:schema]}!")
37
+ AIXM.config.region = options[:region]
37
38
  end
38
39
 
39
40
  # Read the configuration from config.yml.
@@ -89,12 +90,22 @@ module AIPP
89
90
  ).attach_patches.tap(&:parse).detach_patches
90
91
  end
91
92
  end
93
+ if options[:grouped_obstacles]
94
+ info("Grouping obstacles")
95
+ aixm.group_obstacles!
96
+ end
97
+ info("Counting #{aixm.features.count} features")
92
98
  end
93
99
 
94
100
  # Validate the AIXM document.
95
101
  #
96
102
  # @raise [RuntimeError] if the document is not valid
97
103
  def validate_aixm
104
+ info("Detecting duplicates")
105
+ if (duplicates = aixm.features.duplicates).any?
106
+ message = "duplicates found:\n" + duplicates.map { "#{_1.inspect} from #{_1.source}" }.join("\n")
107
+ @options[:force] ? warn(message, pry: binding) : fail(message)
108
+ end
98
109
  info("Validating #{options[:schema].upcase}")
99
110
  unless aixm.valid?
100
111
  message = "invalid #{options[:schema].upcase} document:\n" + aixm.errors.map(&:message).join("\n")
@@ -102,56 +113,60 @@ module AIPP
102
113
  end
103
114
  end
104
115
 
105
- # Write the AIXM document.
106
- def write_aixm
107
- info("Writing #{aixm_file}")
108
- AIXM.config.mid_region = options[:region] if options[:mid]
109
- File.write(aixm_file, aixm.to_xml)
110
- end
111
-
112
116
  # Write the AIXM document and context information.
113
117
  def write_build
114
- info("Writing build")
115
- builds_path.mkpath
116
- build_file = builds_path.join("#{@options[:airac].date.xmlschema}.zip")
117
- build_file.delete if build_file.exist?
118
- Dir.mktmpdir do |tmp_dir|
119
- tmp_dir = Pathname(tmp_dir)
120
- AIXM.config.mid_region = options[:region]
121
- # AIXM/OFMX file
122
- File.write(tmp_dir.join(aixm_file), aixm.to_xml)
123
- # Build details
124
- File.write(
125
- tmp_dir.join('build.yaml'), {
126
- version: AIPP::VERSION,
127
- config: @config,
128
- options: @options
129
- }.to_yaml
130
- )
131
- # Manifest
132
- manifest, buffer, feature, uid, comment = [], '', '', '', ''
133
- File.open(tmp_dir.join(aixm_file)).each do |line|
134
- buffer << line
135
- case line
136
- when /^ {2}<(\w{3}) / then buffer, feature = line, $1
137
- when /^ {4}<#{feature}Uid mid="(.*?)"/ then uid = $1
138
- when /^ {2}<!-- (.*) -->/ then comment = $1
139
- when /^ {2}<\/#{feature}>/
140
- manifest << [feature, uid[0,8], buffer.payload_hash(region: options[:region])[0,8], comment].to_csv
141
- feature, uid = '', ''
118
+ if @options[:aip]
119
+ info ("Skipping build")
120
+ else
121
+ info("Writing build")
122
+ builds_path.mkpath
123
+ build_file = builds_path.join("#{@options[:airac].date.xmlschema}.zip")
124
+ Dir.mktmpdir do |tmp_dir|
125
+ tmp_dir = Pathname(tmp_dir)
126
+ # AIXM/OFMX file
127
+ AIXM.config.mid = true
128
+ File.write(tmp_dir.join(aixm_file), aixm.to_xml)
129
+ # Build details
130
+ File.write(
131
+ tmp_dir.join('build.yaml'), {
132
+ version: AIPP::VERSION,
133
+ config: @config,
134
+ options: @options
135
+ }.to_yaml
136
+ )
137
+ # Manifest
138
+ manifest, buffer, feature, aip, uid, comment = [], '', '', '', '', ''
139
+ File.open(tmp_dir.join(aixm_file)).each do |line|
140
+ buffer << line
141
+ case line
142
+ when /^ {2}<(\w{3}).*source=".*?\|.*?\|(.*?)\|/ then buffer, feature, aip = line, $1, $2
143
+ when /^ {4}<#{feature}Uid[^>]+?mid="(.*?)"/ then uid = $1
144
+ when /^ {2}<!-- (.*) -->/ then comment = $1
145
+ when /^ {2}<\/#{feature}>/
146
+ manifest << [aip, feature, uid[0,8], AIXM::PayloadHash.new(buffer).to_uuid[0,8], comment].to_csv
147
+ feature, aip, uid = '', '', ''
148
+ end
142
149
  end
143
- end
144
- manifest = manifest.sort.prepend "Feature,Short Uid Hash,Short Feature Hash,Comment\n"
145
- File.write(tmp_dir.join('manifest.csv'), manifest.join)
146
- # Zip it
147
- Zip::File.open(build_file, Zip::File::CREATE) do |zip|
148
- tmp_dir.children.each do |entry|
149
- zip.add(entry.basename.to_s, entry) unless entry.basename.to_s[0] == '.'
150
+ manifest = manifest.sort.prepend "AIP,Feature,Short Uid Hash,Short Feature Hash,Comment\n"
151
+ File.write(tmp_dir.join('manifest.csv'), manifest.join)
152
+ # Zip it
153
+ build_file.delete if build_file.exist?
154
+ Zip::File.open(build_file, Zip::File::CREATE) do |zip|
155
+ tmp_dir.children.each do |entry|
156
+ zip.add(entry.basename.to_s, entry) unless entry.basename.to_s[0] == '.'
157
+ end
150
158
  end
151
159
  end
152
160
  end
153
161
  end
154
162
 
163
+ # Write the AIXM document.
164
+ def write_aixm
165
+ info("Writing #{aixm_file}")
166
+ AIXM.config.mid = options[:mid]
167
+ File.write(aixm_file, aixm.to_xml)
168
+ end
169
+
155
170
  # Write the configuration to config.yml.
156
171
  def write_config
157
172
  info("Writing config.yml")
@@ -117,7 +117,7 @@ module AIPP
117
117
  end
118
118
 
119
119
  def page_for(index:)
120
- @page_ranges.index(@page_ranges.bsearch { |i| i >= index }) + 1
120
+ @page_ranges.index(@page_ranges.bsearch { _1 >= index }) + 1
121
121
  end
122
122
  end
123
123
  end
@@ -42,7 +42,7 @@ module AIPP
42
42
  @airport = airport_from tr
43
43
  verbose_info "Parsing #{@airport.id}"
44
44
  ad2_exists = false
45
- if airport = select(:airport, id: @airport.id).first
45
+ if airport = find_by(:airport, id: @airport.id).first
46
46
  ad2_exists = true
47
47
  @airport = airport
48
48
  end
@@ -81,7 +81,7 @@ module AIPP
81
81
  when /ouv.+cap|milit/ then :permitted
82
82
  when /usa.+restr|priv/ then :reservation_required
83
83
  end
84
- @airport.add_usage_limitation(limitation) do |l|
84
+ @airport.add_usage_limitation(type: limitation) do |l|
85
85
  (%w(s ns p) & raw_conditions).each do |raw_purpose|
86
86
  l.add_condition do |c|
87
87
  c.realm = raw_limitation.match?(/milit/) ? :military : :civilian
@@ -111,6 +111,7 @@ module AIPP
111
111
  name: tds[0].text.strip.split.join('/')
112
112
  ).tap do |runway|
113
113
  @runway = runway # TODO: needed for now for surface composition patches to work
114
+ bidirectional = runway.name.include? '/'
114
115
  runway.length = AIXM.d(tds[1].css('span[id$="VAL_LEN"]').text.to_i, :m)
115
116
  runway.width = AIXM.d(tds[1].css('span[id$="VAL_WID"]').text.to_i, :m)
116
117
  unless (text = tds[1].css('span[id*="SURFACE"]').text.compact).blank?
@@ -122,16 +123,16 @@ module AIPP
122
123
  runway.remarks = tds[7].text.cleanup.blank_to_nil
123
124
  values = tds[2].text.remove('°').strip.split
124
125
  runway.forth.geographic_orientation = AIXM.a(values.first.to_i)
125
- runway.back.geographic_orientation = AIXM.a(values.last.to_i)
126
+ runway.back.geographic_orientation = AIXM.a(values.last.to_i) if bidirectional
126
127
  parts = tds[3].text.strip.split(/\n\s+\n\s+/, 2)
127
128
  runway.forth.xy = (xy_from(parts[0]) unless parts[0].blank?)
128
- runway.back.xy = (xy_from(parts[1]) unless parts[1].blank?)
129
+ runway.back.xy = (xy_from(parts[1]) unless parts[1].blank?) if bidirectional
129
130
  values = tds[4].text.strip.split
130
131
  runway.forth.z = AIXM.z(values.first.to_i, :qnh)
131
- runway.back.z = AIXM.z(values.last.to_i, :qnh)
132
+ runway.back.z = AIXM.z(values.last.to_i, :qnh) if bidirectional
132
133
  displaced_thresholds = displaced_thresholds_from(tds[5])
133
134
  runway.forth.displaced_threshold = displaced_thresholds.first
134
- runway.back.displaced_threshold = displaced_thresholds.last
135
+ runway.back.displaced_threshold = displaced_thresholds.last if bidirectional
135
136
  end
136
137
  end
137
138