aipp 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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