aipp 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +122 -37
  6. data/TODO.md +4 -0
  7. data/aipp.gemspec +8 -3
  8. data/lib/aipp.rb +14 -2
  9. data/lib/aipp/aip.rb +44 -29
  10. data/lib/aipp/downloader.rb +115 -0
  11. data/lib/aipp/executable.rb +6 -6
  12. data/lib/aipp/parser.rb +23 -23
  13. data/lib/aipp/patcher.rb +47 -0
  14. data/lib/aipp/pdf.rb +123 -0
  15. data/lib/aipp/regions/LF/AD-1.3.rb +162 -0
  16. data/lib/aipp/regions/LF/AD-1.3.yml +511 -0
  17. data/lib/aipp/regions/LF/AD-1.6.rb +31 -0
  18. data/lib/aipp/regions/LF/AD-2.rb +316 -0
  19. data/lib/aipp/regions/LF/AD-2.yml +185 -0
  20. data/lib/aipp/regions/LF/AD-3.1.rb-NEW +11 -0
  21. data/lib/aipp/regions/LF/ENR-2.1.rb +25 -24
  22. data/lib/aipp/regions/LF/ENR-4.1.rb +24 -23
  23. data/lib/aipp/regions/LF/ENR-4.3.rb +8 -6
  24. data/lib/aipp/regions/LF/ENR-5.1.rb +32 -22
  25. data/lib/aipp/regions/LF/ENR-5.5.rb-NEW +11 -0
  26. data/lib/aipp/regions/LF/helpers/AD_radio.rb +90 -0
  27. data/lib/aipp/regions/LF/helpers/URL.rb +26 -0
  28. data/lib/aipp/regions/LF/helpers/common.rb +186 -0
  29. data/lib/aipp/version.rb +1 -1
  30. data/lib/core_ext/enumerable.rb +52 -0
  31. data/lib/core_ext/nil_class.rb +10 -0
  32. data/lib/core_ext/object.rb +42 -0
  33. data/lib/core_ext/string.rb +105 -0
  34. data/spec/fixtures/archive.zip +0 -0
  35. data/spec/fixtures/document.pdf +0 -0
  36. data/spec/fixtures/document.pdf.json +1 -0
  37. data/spec/fixtures/new.html +6 -0
  38. data/spec/fixtures/new.pdf +0 -0
  39. data/spec/fixtures/new.txt +1 -0
  40. data/spec/lib/aipp/downloader_spec.rb +81 -0
  41. data/spec/lib/aipp/patcher_spec.rb +46 -0
  42. data/spec/lib/aipp/pdf_spec.rb +124 -0
  43. data/spec/lib/core_ext/enumberable_spec.rb +76 -0
  44. data/spec/lib/core_ext/nil_class_spec.rb +11 -0
  45. data/spec/lib/core_ext/string_spec.rb +88 -0
  46. data/spec/spec_helper.rb +1 -0
  47. metadata +123 -23
  48. data/lib/aipp/progress.rb +0 -40
  49. data/lib/aipp/refinements.rb +0 -114
  50. data/lib/aipp/regions/LF/helper.rb +0 -177
  51. data/spec/lib/aipp/refinements_spec.rb +0 -123
@@ -1,40 +0,0 @@
1
- module AIPP
2
- module Progress
3
-
4
- # Issue an informational message.
5
- #
6
- # @param message [String] informational message
7
- # @param force [Boolean] whether to show the message only when in verbose mode
8
- # @param color [Symbol] override default color
9
- def info(message, force: false, color: nil)
10
- case
11
- when !force && options[:verbose]
12
- color ||= :blue
13
- puts message.send(color)
14
- when force
15
- color ||= :black
16
- puts message.send(color)
17
- end
18
- end
19
-
20
- # Issue a warning and maybe open a Pry session in the context of the error
21
- # or binding passed.
22
- #
23
- # @example with error context
24
- # begin
25
- # (...)
26
- # rescue => error
27
- # warn("oops", context: error)
28
- # end
29
- # @example with binding context
30
- # warn("oops", context: binding)
31
- # @param message [String] warning message
32
- # @param context [Exception, Binding, nil] error or binding object
33
- def warn(message, context: nil)
34
- $WARN_COUNTER = $WARN_COUNTER.to_i + 1
35
- Kernel.warn "WARNING #{$WARN_COUNTER}: #{message}".red
36
- Pry::rescued(context) if context && options[:pry_on_warn] == $WARN_COUNTER
37
- end
38
-
39
- end
40
- end
@@ -1,114 +0,0 @@
1
- module AIPP
2
- module Refinements
3
-
4
- # @!method blank_to_nil
5
- # Convert blank strings to +nil+.
6
- #
7
- # @example
8
- # "foobar".blank_to_nil # => "foobar"
9
- # " ".blank_to_nil # => nil
10
- # "".blank_to_nil # => nil
11
- # nil.blank_to_nil # => nil
12
- #
13
- # @note This is a refinement for +String+ and +NilClass+
14
- # @return [String, nil] converted string
15
- refine String do
16
- def blank_to_nil
17
- match?(/\A\s*\z/) ? nil : self
18
- end
19
- end
20
-
21
- # Always returns +nil+, companion to +String#blank_to_nil+.
22
- refine NilClass do
23
- def blank_to_nil
24
- nil
25
- end
26
- end
27
-
28
- # @!method blank?
29
- # Check whether the string is blank.
30
- #
31
- # @example
32
- # "foobar".blank? # => false
33
- # " ".blank? # => true
34
- # "".blank? # => true
35
- # nil.blank? # => true
36
- #
37
- # @note This is a refinement for +String+ and +NilClass+
38
- # @return [Boolean] whether the string is blank or not
39
- refine String do
40
- def blank?
41
- !blank_to_nil
42
- end
43
- end
44
-
45
- # Always returns +true+, companion to +String#blank?+.
46
- refine NilClass do
47
- def blank?
48
- true
49
- end
50
- end
51
-
52
- # @!method classify
53
- # Convert file name to class name.
54
- #
55
- # @example
56
- # "ENR-5.1".classify # => "ENR51"
57
- # "helper".classify # => "Helper"
58
- # "foo_bar".classify # => "FooBar"
59
- #
60
- # @note This is a refinement for +String+
61
- # @return [String] converted string
62
- refine String do
63
- def classify
64
- gsub(/\W/, '').gsub(/(?:^|_)(\w)/) { $1.upcase }
65
- end
66
- end
67
-
68
- # @!method constantize
69
- # Get constant for array containing the lookup path.
70
- #
71
- # @example
72
- # %w(AIPP AIRAC).constantize # => AIPP::AIRAC
73
- #
74
- # @note This is a refinement for +Array+
75
- # @return [Class] converted array
76
- refine Array do
77
- def constantize
78
- Kernel.const_get(self.join('::'))
79
- end
80
- end
81
-
82
- # @!method split(object=nil, &block)
83
- # Divides an enumerable into sub-enumerables based on a delimiter,
84
- # returning an array of these sub-enumerables.
85
- #
86
- # It takes the same arguments as +Enumerable#find_index+ and suppresses
87
- # trailing zero-length sub-enumerator as does +String#split+.
88
- #
89
- # @example
90
- # [1, 2, 0, 3, 4].split { |e| e == 0 } # => [[1, 2], [3, 4]]
91
- # [1, 2, 0, 3, 4].split(0) # => [[1, 2], [3, 4]]
92
- # [0, 0, 1, 0, 2].split(0) # => [[], [] [1], [2]]
93
- # [1, 0, 0, 2, 3].split(0) # => [[1], [], [2], [3]]
94
- # [1, 0, 2, 0, 0].split(0) # => [[1], [2]]
95
- #
96
- # @note This is a refinement for +Enumerable+
97
- # @param object [Object] element at which to split
98
- # @yield [Object] element to analyze
99
- # @yieldreturn [Boolean] whether to split at this element or not
100
- # @return [Array]
101
- refine Enumerable do
102
- def split(*args, &block)
103
- [].tap do |array|
104
- while index = slice((start ||= 0)...length).find_index(*args, &block)
105
- array << slice(start...start+index)
106
- start += index + 1
107
- end
108
- array << slice(start..-1) if start < length
109
- end
110
- end
111
- end
112
-
113
- end
114
- end
@@ -1,177 +0,0 @@
1
- module AIPP
2
- module LF
3
- module Helper
4
- using AIPP::Refinements
5
- using AIXM::Refinements
6
-
7
- BORDERS = {
8
- 'franco-allemande' => 'FRANCE_GERMANY',
9
- 'franco-espagnole' => 'FRANCE_SPAIN',
10
- 'franco-italienne' => 'FRANCE_ITALY',
11
- 'franco-suisse' => 'FRANCE_SWITZERLAND',
12
- 'franco-luxembourgeoise' => 'FRANCE_LUXEMBOURG',
13
- 'franco-belge' => 'BELGIUM_FRANCE',
14
- 'germano-suisse' => 'GERMANY_SWITZERLAND',
15
- 'hispano-andorrane' => 'ANDORRA_SPAIN',
16
- 'la côte atlantique française' => 'FRANCE_ATLANTIC_COAST',
17
- 'côte méditérrannéenne' => 'FRANCE_MEDITERRANEAN_COAST',
18
- 'limite des eaux territoriales atlantique françaises' => 'FRANCE_ATLANTIC_TERRITORIAL_SEA',
19
- 'parc national des écrins' => 'FRANCE_ECRINS_NATIONAL_PARK'
20
- }.freeze
21
-
22
- INTERSECTIONS = {
23
- 'FRANCE_SPAIN|ANDORRA_SPAIN' => AIXM.xy(lat: 42.502720, long: 1.725965),
24
- 'ANDORRA_SPAIN|FRANCE_SPAIN' => AIXM.xy(lat: 42.603571, long: 1.442681),
25
- 'FRANCE_SWITZERLAND|FRANCE_ITALY' => AIXM.xy(lat: 45.922701, long: 7.044125),
26
- 'BELGIUM_FRANCE|FRANCE_LUXEMBOURG' => AIXM.xy(lat: 49.546428, long: 5.818415),
27
- 'FRANCE_LUXEMBOURG|FRANCE_GERMANY' => AIXM.xy(lat: 49.469438, long: 6.367516),
28
- 'FRANCE_GERMANY|FRANCE_SWITZERLAND' => AIXM.xy(lat: 47.589831, long: 7.589049),
29
- 'GERMANY_SWITZERLAND|FRANCE_GERMANY' => AIXM.xy(lat: 47.589831, long: 7.589049)
30
- }
31
-
32
- ANGLICISE_MAP = {
33
- /[^A-Z0-9 .\-]/ => '',
34
- / 0(\d)/ => ' \1',
35
- /(\d)-(\d)/ => '\1.\2',
36
- /PARTIE/ => '',
37
- /DELEG\./ => 'DELEG ',
38
- /FRANCAISE?/ => 'FR',
39
- /ANGLAISE?/ => 'UK',
40
- /BELGE/ => 'BE',
41
- /LUXEMBOURGEOISE?/ => 'LU',
42
- /ALLEMANDE?/ => 'DE',
43
- /SUISSE/ => 'CH',
44
- /ITALIEN(?:NE)?/ => 'IT',
45
- /ESPAGNOLE?/ => 'ES',
46
- /ANDORRANE?/ => 'AD',
47
- /NORD/ => 'N',
48
- /EST/ => 'E',
49
- /SUD/ => 'S',
50
- /OEST/ => 'W',
51
- /ANGLO NORMANDES/ => 'ANGLO-NORMANDES',
52
- / +/ => ' '
53
- }.freeze
54
-
55
- # Download URL
56
-
57
- def url_for(aip_file)
58
- "https://www.sia.aviation-civile.gouv.fr/dvd/eAIP_%s/FRANCE/AIRAC-%s/html/eAIP/FR-%s-fr-FR.html" % [
59
- options[:airac].date.strftime('%d_%^b_%Y'), # 04_JAN_2018
60
- options[:airac].date.xmlschema, # 2018-01-04
61
- aip_file # ENR-5.1 or AD-2.LFMV
62
- ]
63
- end
64
-
65
- # Templates
66
-
67
- def organisation_lf
68
- @organisation_lf ||= AIXM.organisation(
69
- name: 'FRANCE',
70
- type: 'S'
71
- ).tap do |organisation|
72
- organisation.id = 'LF'
73
- end
74
- end
75
-
76
- # Transformations
77
-
78
- def cleanup(node:)
79
- node.tap do |root|
80
- root.css('del').each { |n| n.remove } # remove deleted entries
81
- end
82
- end
83
-
84
- def anglicise(name:)
85
- name.uptrans.tap do |string|
86
- ANGLICISE_MAP.each do |regexp, replacement|
87
- string.gsub!(regexp, replacement)
88
- end
89
- end
90
- end
91
-
92
- # Parsers
93
-
94
- def source_for(element)
95
- [
96
- options[:region],
97
- @aip.split('-').first,
98
- @aip,
99
- options[:airac].date.xmlschema,
100
- element.line
101
- ].join('|')
102
- end
103
-
104
- def xy_from(td)
105
- parts = td.text.strip.split(/\s+/)
106
- AIXM.xy(lat: parts[0], long: parts[1])
107
- end
108
-
109
- def z_from(limit)
110
- case limit
111
- when nil then nil
112
- when 'SFC' then AIXM::GROUND
113
- when 'UNL' then AIXM::UNLIMITED
114
- when /(\d+)ftASFC/ then AIXM.z($1.to_i, :qfe)
115
- when /(\d+)ftAMSL/ then AIXM.z($1.to_i, :qnh)
116
- when /FL(\d+)/ then AIXM.z($1.to_i, :qne)
117
- else fail "z `#{limit}' not recognized"
118
- end
119
- end
120
-
121
- def layer_from(td)
122
- above, below = td.text.gsub(/ /, '').split(/\n+/).select(&:blank_to_nil).split { |e| e.match? '---+' }
123
- above.reverse!
124
- AIXM.layer(
125
- vertical_limits: AIXM.vertical_limits(
126
- max_z: z_from(above[1]),
127
- upper_z: z_from(above[0]),
128
- lower_z: z_from(below[0]),
129
- min_z: z_from(below[1])
130
- )
131
- )
132
- end
133
-
134
- def geometry_from(td)
135
- AIXM.geometry.tap do |geometry|
136
- buffer = {}
137
- td.text.gsub(/\s+/, ' ').strip.split(/ - /).append('end').each do |element|
138
- case element
139
- when /arc (anti-)?horaire .+ sur (\S+) , (\S+)/i
140
- geometry << AIXM.arc(
141
- xy: buffer.delete(:xy),
142
- center_xy: AIXM.xy(lat: $2, long: $3),
143
- clockwise: $1.nil?
144
- )
145
- when /cercle de ([\d\.]+) (NM|km|m) .+ sur (\S+) , (\S+)/i
146
- geometry << AIXM.circle(
147
- center_xy: AIXM.xy(lat: $3, long: $4),
148
- radius: AIXM.d($1.to_f, $2)
149
- )
150
- when /end|(\S+) , (\S+)/
151
- geometry << AIXM.point(xy: buffer[:xy]) if buffer.has_key?(:xy)
152
- buffer[:xy] = AIXM.xy(lat: $1, long: $2) if $1
153
- when /^frontière ([\w-]+)/i, /^(\D[^(]+)/i
154
- border_name = BORDERS.fetch($1.downcase.strip)
155
- buffer[:xy] ||= INTERSECTIONS.fetch("#{buffer[:border_name]}|#{border_name}")
156
- buffer[:border_name] = border_name
157
- if border_name == 'FRANCE_SPAIN' # specify which part of this split border
158
- border_name += buffer[:xy].lat < 42.55 ? '_EAST' : '_WEST'
159
- end
160
- geometry << AIXM.border(
161
- xy: buffer.delete(:xy),
162
- name: border_name
163
- )
164
- else
165
- fail "geometry `#{element}' not recognized"
166
- end
167
- end
168
- end
169
- end
170
-
171
- def timetable_from(td)
172
- AIXM::H24 if td.text.gsub(/\W/, '') == 'H24'
173
- end
174
-
175
- end
176
- end
177
- end
@@ -1,123 +0,0 @@
1
- require_relative '../../spec_helper'
2
-
3
- using AIPP::Refinements
4
-
5
- describe AIPP::Refinements do
6
-
7
- context String do
8
- describe :blank_to_nil do
9
- it "must convert blank to nil" do
10
- "\n \n ".blank_to_nil.must_be :nil?
11
- end
12
-
13
- it "must leave non-blank untouched" do
14
- "foobar".blank_to_nil.must_equal "foobar"
15
- end
16
-
17
- it "must leave non-blank with whitespace untouched" do
18
- "\nfoo bar\n".blank_to_nil.must_equal "\nfoo bar\n"
19
- end
20
- end
21
-
22
- describe :blank? do
23
- it "all whitespace must return true" do
24
- "\n \n ".blank?.must_equal true
25
- end
26
-
27
- it "not all whitespace must return false" do
28
- "\nfoo bar\n".blank?.must_equal false
29
- end
30
- end
31
-
32
- describe :classify do
33
- it "must convert file name to class name" do
34
- "ENR-5.1".classify.must_equal "ENR51"
35
- "helper".classify.must_equal "Helper"
36
- "foo_bar".classify.must_equal "FooBar"
37
- end
38
- end
39
- end
40
-
41
- context NilClass do
42
- describe :blank_to_nil do
43
- it "must return self" do
44
- nil.blank_to_nil.must_be :nil?
45
- end
46
- end
47
-
48
- describe :blank? do
49
- it "must return true" do
50
- nil.blank?.must_equal true
51
- end
52
- end
53
- end
54
-
55
- context Array do
56
- describe :constantize do
57
- it "must convert to constant" do
58
- %w(AIPP Refinements).constantize.must_equal AIPP::Refinements
59
- end
60
-
61
- it "fails to convert to inexistant constant" do
62
- -> { %w(Foo Bar).constantize }.must_raise NameError
63
- end
64
- end
65
- end
66
-
67
- context Enumerable do
68
- describe :split do
69
- context "by object" do
70
- it "must split at matching element" do
71
- [1, 2, 0, 3, 4].split(0).must_equal [[1, 2], [3, 4]]
72
- end
73
-
74
- it "won't split when no element matches" do
75
- [1, 2, 3].split(0).must_equal [[1, 2, 3]]
76
- end
77
-
78
- it "won't split zero length enumerable" do
79
- [].split(0).must_equal []
80
- end
81
-
82
- it "must keep leading empty subarrays" do
83
- [0, 1, 2, 0, 3, 4].split(0).must_equal [[], [1, 2], [3, 4]]
84
- end
85
-
86
- it "must keep empty subarrays in the middle" do
87
- [1, 2, 0, 0, 3, 4].split(0).must_equal [[1, 2], [], [3, 4]]
88
- end
89
-
90
- it "must drop trailing empty subarrays" do
91
- [1, 2, 0, 3, 4, 0].split(0).must_equal [[1, 2], [3, 4]]
92
- end
93
- end
94
-
95
- context "by block" do
96
- it "must split at matching element" do
97
- [1, 2, 0, 3, 4].split { |e| e.zero? }.must_equal [[1, 2], [3, 4]]
98
- end
99
-
100
- it "won't split when no element matches" do
101
- [1, 2, 3].split { |e| e.zero? }.must_equal [[1, 2, 3]]
102
- end
103
-
104
- it "won't split zero length enumerable" do
105
- [].split { |e| e.zero? }.must_equal []
106
- end
107
-
108
- it "must keep leading empty subarrays" do
109
- [0, 1, 2, 0, 3, 4].split { |e| e.zero? }.must_equal [[], [1, 2], [3, 4]]
110
- end
111
-
112
- it "must keep empty subarrays in the middle" do
113
- [1, 2, 0, 0, 3, 4].split { |e| e.zero? }.must_equal [[1, 2], [], [3, 4]]
114
- end
115
-
116
- it "must drop trailing empty subarrays" do
117
- [1, 2, 0, 3, 4, 0].split { |e| e.zero? }.must_equal [[1, 2], [3, 4]]
118
- end
119
- end
120
- end
121
- end
122
-
123
- end