aipp 0.2.2 → 0.2.3

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.
@@ -15,10 +15,10 @@ module AIPP
15
15
  'franco-belge' => 'BELGIUM_FRANCE',
16
16
  'germano-suisse' => 'GERMANY_SWITZERLAND',
17
17
  'hispano-andorrane' => 'ANDORRA_SPAIN',
18
- 'la côte atlantique française' => 'FRANCE_ATLANTIC_COAST', # TODO: handle internally
19
- 'côte méditérrannéenne' => 'FRANCE_MEDITERRANEAN_COAST', # TODO: handle internally
20
- 'limite des eaux territoriales atlantique françaises' => 'FRANCE_ATLANTIC_TERRITORIAL_SEA', # TODO: handle internally
21
- 'parc national des écrins' => 'FRANCE_ECRINS_NATIONAL_PARK' # TODO: handle internally
18
+ 'la côte atlantique française' => 'FRANCE_ATLANTIC_COAST',
19
+ 'côte méditérrannéenne' => 'FRANCE_MEDITERRANEAN_COAST',
20
+ 'limite des eaux territoriales atlantique françaises' => 'FRANCE_ATLANTIC_TERRITORIAL_SEA',
21
+ 'parc national des écrins' => 'FRANCE_ECRINS_NATIONAL_PARK'
22
22
  }.freeze
23
23
 
24
24
  # Intersection points between three countries
@@ -127,12 +127,11 @@ module AIPP
127
127
 
128
128
  def layer_from(text_for_limits, text_for_class=nil)
129
129
  above, below = text_for_limits.gsub(/ /, '').split(/\n+/).select(&:blank_to_nil).split { |e| e.match? '---+' }
130
- above.reverse!
131
130
  AIXM.layer(
132
131
  class: text_for_class,
133
132
  vertical_limits: AIXM.vertical_limits(
134
- max_z: z_from(above[1]),
135
133
  upper_z: z_from(above[0]),
134
+ max_z: z_from(above[1]),
136
135
  lower_z: z_from(below[0]),
137
136
  min_z: z_from(below[1])
138
137
  )
@@ -158,17 +157,26 @@ module AIPP
158
157
  when /end|(\S+) , (\S+)/
159
158
  geometry << AIXM.point(xy: buffer[:xy]) if buffer.has_key?(:xy)
160
159
  buffer[:xy] = AIXM.xy(lat: $1, long: $2) if $1
160
+ if border = buffer.delete(:border)
161
+ from = border.nearest(xy: geometry.segments.last.xy)
162
+ to = border.nearest(xy: buffer[:xy], geometry_index: from.geometry_index)
163
+ geometry.concat border.segment(from_position: from, to_position: to).map(&:to_point)
164
+ end
161
165
  when /^frontière ([\w-]+)/i, /^(\D[^(]+)/i
162
166
  border_name = BORDERS.fetch($1.downcase.strip)
163
- buffer[:xy] ||= INTERSECTIONS.fetch("#{buffer[:border_name]}|#{border_name}")
164
- buffer[:border_name] = border_name
165
- if border_name == 'FRANCE_SPAIN' # specify which part of this split border
166
- border_name += buffer[:xy].lat < 42.55 ? '_EAST' : '_WEST'
167
+ if borders.has_key? border_name # border from GeoJSON
168
+ buffer[:border] = borders[border_name]
169
+ else # named border
170
+ buffer[:xy] ||= INTERSECTIONS.fetch("#{buffer[:border_name]}|#{border_name}")
171
+ buffer[:border_name] = border_name
172
+ if border_name == 'FRANCE_SPAIN' # specify which part of this split border
173
+ border_name += buffer[:xy].lat < 42.55 ? '_EAST' : '_WEST'
174
+ end
175
+ geometry << AIXM.border(
176
+ xy: buffer.delete(:xy),
177
+ name: border_name
178
+ )
167
179
  end
168
- geometry << AIXM.border(
169
- xy: buffer.delete(:xy),
170
- name: border_name
171
- )
172
180
  else
173
181
  fail "geometry `#{element}' not recognized"
174
182
  end
@@ -176,8 +184,10 @@ module AIPP
176
184
  end
177
185
  end
178
186
 
179
- def timetable_from(text)
180
- AIXM::H24 if text.gsub(/\W/, '') == 'H24'
187
+ def timetable_from!(text)
188
+ if text.gsub!(/^\s*#{AIXM::H_RE}\s*$/, '')
189
+ AIXM.timetable(code: Regexp.last_match&.to_s&.strip)
190
+ end
181
191
  end
182
192
 
183
193
  end
@@ -1,3 +1,3 @@
1
1
  module AIPP
2
- VERSION = "0.2.2".freeze
2
+ VERSION = "0.2.3".freeze
3
3
  end
@@ -0,0 +1,16 @@
1
+ class Integer
2
+
3
+ # Iterates the given block, passing in increasing or decreasing values to and
4
+ # including limit
5
+ #
6
+ # If no block is given, an Enumerator is returned instead.
7
+ #
8
+ # @example
9
+ # 10.up_or_downto(12).to_a # => [10, 11, 12]
10
+ # 10.upto(12).to_a # => [10, 11, 12]
11
+ # 10.up_or_downto(8).to_a # => [10, 9, 8]
12
+ # 10.downto(8).to_a # => [10, 9, 8]
13
+ def up_or_downto(limit)
14
+ self > limit ? self.downto(limit) : self.upto(limit)
15
+ end
16
+ end
@@ -32,11 +32,11 @@ class Object
32
32
  puts message.send(color)
33
33
  end
34
34
 
35
- # Issue a debug message.
35
+ # Issue a verbose informational message.
36
36
  #
37
- # @param message [String] debug message
38
- def debug(message, color: :blue)
39
- info(message, color: color) if $DEBUG
37
+ # @param message [String] verbose informational message
38
+ def verbose_info(message, color: :blue)
39
+ info(message, color: color) if $VERBOSE_INFO
40
40
  end
41
41
 
42
42
  end
@@ -0,0 +1,201 @@
1
+ {
2
+ "type": "GeometryCollection",
3
+ "geometries": [
4
+ {
5
+ "type": "LineString",
6
+ "coordinates": [
7
+ [
8
+ 4.757294654846191,
9
+ 43.989202079482276
10
+ ],
11
+ [
12
+ 4.764375686645508,
13
+ 43.99701327763528
14
+ ],
15
+ [
16
+ 4.7519731521606445,
17
+ 44.00269350325321
18
+ ],
19
+ [
20
+ 4.745192527770996,
21
+ 43.99506829280446
22
+ ],
23
+ [
24
+ 4.751286506652832,
25
+ 43.99235138187902
26
+ ],
27
+ [
28
+ 4.750943183898926,
29
+ 43.99198088438825
30
+ ],
31
+ [
32
+ 4.757294654846191,
33
+ 43.989202079482276
34
+ ]
35
+ ]
36
+ },
37
+ {
38
+ "type": "LineString",
39
+ "coordinates": [
40
+ [
41
+ 4.777421951293944,
42
+ 44.00115001749186
43
+ ],
44
+ [
45
+ 4.78205680847168,
46
+ 43.994111213373934
47
+ ],
48
+ [
49
+ 4.784030914306641,
50
+ 43.99818641226534
51
+ ],
52
+ [
53
+ 4.787635803222656,
54
+ 44.00077957493397
55
+ ],
56
+ [
57
+ 4.785747528076171,
58
+ 44.00448389642906
59
+ ],
60
+ [
61
+ 4.790983200073242,
62
+ 44.004669106432225
63
+ ],
64
+ [
65
+ 4.798364639282227,
66
+ 44.008373185063874
67
+ ],
68
+ [
69
+ 4.793901443481445,
70
+ 44.01528684632061
71
+ ],
72
+ [
73
+ 4.787378311157227,
74
+ 44.01584237340163
75
+ ],
76
+ [
77
+ 4.785575866699219,
78
+ 44.01960747533136
79
+ ],
80
+ [
81
+ 4.768667221069335,
82
+ 44.01831131968508
83
+ ],
84
+ [
85
+ 4.763689041137695,
86
+ 44.01460786170962
87
+ ],
88
+ [
89
+ 4.760427474975586,
90
+ 44.01065725159039
91
+ ],
92
+ [
93
+ 4.770212173461914,
94
+ 44.002940457248556
95
+ ],
96
+ [
97
+ 4.777421951293944,
98
+ 44.00115001749186
99
+ ]
100
+ ]
101
+ },
102
+ {
103
+ "type": "LineString",
104
+ "coordinates": [
105
+ [
106
+ 4.752960205078125,
107
+ 43.93721446391471
108
+ ],
109
+ [
110
+ 4.744377136230469,
111
+ 43.950068873803815
112
+ ],
113
+ [
114
+ 4.737510681152343,
115
+ 43.97033364196856
116
+ ],
117
+ [
118
+ 4.7344207763671875,
119
+ 43.98713332912919
120
+ ],
121
+ [
122
+ 4.7371673583984375,
123
+ 44.00516299694704
124
+ ],
125
+ [
126
+ 4.743347167968749,
127
+ 44.02195282780904
128
+ ],
129
+ [
130
+ 4.749870300292969,
131
+ 44.037503870182896
132
+ ],
133
+ [
134
+ 4.755706787109375,
135
+ 44.05379106204314
136
+ ],
137
+ [
138
+ 4.7646331787109375,
139
+ 44.070073775703484
140
+ ]
141
+ ]
142
+ },
143
+ {
144
+ "type": "LineString",
145
+ "coordinates": [
146
+ [
147
+ 4.7948455810546875,
148
+ 43.95328204198018
149
+ ],
150
+ [
151
+ 4.801368713378906,
152
+ 43.956989327857265
153
+ ],
154
+ [
155
+ 4.815788269042969,
156
+ 43.9646503190861
157
+ ],
158
+ [
159
+ 4.82025146484375,
160
+ 43.98614524381678
161
+ ],
162
+ [
163
+ 4.840850830078125,
164
+ 43.98491011404692
165
+ ],
166
+ [
167
+ 4.845314025878906,
168
+ 43.99479043262446
169
+ ],
170
+ [
171
+ 4.8538970947265625,
172
+ 43.98367495857784
173
+ ],
174
+ [
175
+ 4.851493835449218,
176
+ 43.967121395851485
177
+ ],
178
+ [
179
+ 4.8442840576171875,
180
+ 43.96069638244953
181
+ ],
182
+ [
183
+ 4.829521179199219,
184
+ 43.96069638244953
185
+ ],
186
+ [
187
+ 4.819221496582031,
188
+ 43.95501213750488
189
+ ],
190
+ [
191
+ 4.805145263671875,
192
+ 43.955506441260546
193
+ ],
194
+ [
195
+ 4.799995422363281,
196
+ 43.952046228624724
197
+ ]
198
+ ]
199
+ }
200
+ ]
201
+ }
@@ -0,0 +1,135 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe AIPP::Border::Position do
4
+ subject do
5
+ AIPP::Border::Position.new(
6
+ geometries: [
7
+ [AIXM.xy(long: 0, lat: 0), AIXM.xy(long: 1, lat: 1), AIXM.xy(long: 2, lat: 2)],
8
+ [AIXM.xy(long: 10, lat: 10), AIXM.xy(long: 11, lat: 11), AIXM.xy(long: 12, lat: 12)]
9
+ ],
10
+ geometry_index: 0,
11
+ coordinates_index: 0
12
+ )
13
+ end
14
+
15
+ describe :xy do
16
+ it "returns the coordinates" do
17
+ subject.xy.must_equal AIXM.xy(long: 0, lat: 0)
18
+ end
19
+
20
+ it "returns nil if the geometry index is out of bounds" do
21
+ subject.tap { |s| s.geometry_index = 2 }.xy.must_be_nil
22
+ end
23
+
24
+ it "returns nil if the coordinates index is out of bounds" do
25
+ subject.tap { |s| s.coordinates_index = 3 }.xy.must_be_nil
26
+ end
27
+ end
28
+ end
29
+
30
+ describe AIPP::Border do
31
+ let :fixtures_dir do
32
+ Pathname(__FILE__).join('..', '..', '..', 'fixtures')
33
+ end
34
+
35
+ # The border.geojson fixture defines three geometries:
36
+ # * index 0: closed geometry circumventing the airfield of Pujaut
37
+ # * index 1: closed geometry circumventing the village of Pujaut
38
+ # * index 2: unclosed I-shaped geometry following the TGV from the S to N bridges over the Rhône
39
+ # * index 3: unclosed U-shaped geometry around Île de Bartelasse from N to S end of Pont Daladier
40
+ subject do
41
+ AIPP::Border.new(fixtures_dir.join('border.geojson'))
42
+ end
43
+
44
+ describe :initialize do
45
+ it "fails for files unless the extension is .geojson" do
46
+ -> { AIPP::Border.new("/path/to/another.txt") }.must_raise ArgumentError
47
+ end
48
+ end
49
+
50
+ describe :name do
51
+ it "returns the upcased file name" do
52
+ subject.name.must_equal 'BORDER'
53
+ end
54
+ end
55
+
56
+ describe :closed? do
57
+ it "returns true for closed geometries" do
58
+ subject.closed?(geometry_index: 0).must_equal true
59
+ subject.closed?(geometry_index: 1).must_equal true
60
+ end
61
+
62
+ it "returns false for unclosed geometries" do
63
+ subject.closed?(geometry_index: 2).must_equal false
64
+ subject.closed?(geometry_index: 3).must_equal false
65
+ end
66
+ end
67
+
68
+ describe :nearest do
69
+ let :point do
70
+ AIXM.xy(lat: 44.008187986625636, long: 4.759397506713866)
71
+ end
72
+
73
+ it "finds the nearest position on any geometry" do
74
+ position = subject.nearest(xy: point)
75
+ position.geometry_index.must_equal 1
76
+ position.coordinates_index.must_equal 12
77
+ position.xy.must_equal AIXM.xy(lat: 44.01065725159039, long: 4.760427474975586)
78
+ end
79
+
80
+ it "finds the nearest postition on a given geometry" do
81
+ position = subject.nearest(xy: point, geometry_index: 0)
82
+ position.geometry_index.must_equal 0
83
+ position.coordinates_index.must_equal 2
84
+ position.xy.must_equal AIXM.xy(lat: 44.00269350325321, long: 4.7519731521606445)
85
+ end
86
+ end
87
+
88
+ describe :segment do
89
+ it "fails if positions are not on the same geometry" do
90
+ from_position = AIPP::Border::Position.new(geometries: subject.geometries, geometry_index: 0, coordinates_index: 0)
91
+ to_position = AIPP::Border::Position.new(geometries: subject.geometries, geometry_index: 1, coordinates_index: 0)
92
+ -> { subject.segment(from_position: from_position, to_position: to_position) }.must_raise ArgumentError
93
+ end
94
+
95
+ it "returns shortest segment on an unclosed I-shaped geometry" do
96
+ from_position = subject.nearest(xy: AIXM.xy(lat: 44.002940457248556, long: 4.734249114990234))
97
+ to_position = subject.nearest(xy: AIXM.xy(lat: 44.07155380033749, long: 4.7687530517578125), geometry_index: from_position.geometry_index)
98
+ subject.segment(from_position: from_position, to_position: to_position).must_equal [
99
+ AIXM.xy(lat: 44.00516299694704, long: 4.7371673583984375),
100
+ AIXM.xy(lat: 44.02195282780904, long: 4.743347167968749),
101
+ AIXM.xy(lat: 44.037503870182896, long: 4.749870300292969),
102
+ AIXM.xy(lat: 44.05379106204314, long: 4.755706787109375),
103
+ AIXM.xy(lat: 44.070073775703484, long: 4.7646331787109375)
104
+ ]
105
+ end
106
+
107
+ it "returns shortest segment on an unclosed U-shaped geometry" do
108
+ from_position = subject.nearest(xy: AIXM.xy(lat: 43.96563876212758, long: 4.8126983642578125))
109
+ to_position = subject.nearest(xy: AIXM.xy(lat: 43.956989327857265, long: 4.83123779296875), geometry_index: from_position.geometry_index)
110
+ subject.segment(from_position: from_position, to_position: to_position).must_equal [
111
+ AIXM.xy(lat: 43.9646503190861, long: 4.815788269042969),
112
+ AIXM.xy(lat: 43.98614524381678, long: 4.82025146484375),
113
+ AIXM.xy(lat: 43.98491011404692, long: 4.840850830078125),
114
+ AIXM.xy(lat: 43.99479043262446, long: 4.845314025878906),
115
+ AIXM.xy(lat: 43.98367495857784, long: 4.8538970947265625),
116
+ AIXM.xy(lat: 43.967121395851485, long: 4.851493835449218),
117
+ AIXM.xy(lat: 43.96069638244953, long: 4.8442840576171875),
118
+ AIXM.xy(lat: 43.96069638244953, long: 4.829521179199219)
119
+ ]
120
+ end
121
+
122
+ it "returns shortest segment ignoring endings on a closed geometry" do
123
+ from_position = subject.nearest(xy: AIXM.xy(lat: 44.00022390676026, long: 4.789009094238281))
124
+ to_position = subject.nearest(xy: AIXM.xy(lat: 43.99800118202362, long: 4.765834808349609), geometry_index: from_position.geometry_index)
125
+ subject.segment(from_position: from_position, to_position: to_position).must_equal [
126
+ AIXM.xy(lat: 44.00077957493397, long: 4.787635803222656),
127
+ AIXM.xy(lat: 43.99818641226534, long: 4.784030914306641),
128
+ AIXM.xy(lat: 43.994111213373934, long: 4.78205680847168),
129
+ AIXM.xy(lat: 44.00115001749186, long: 4.777421951293944),
130
+ AIXM.xy(lat: 44.002940457248556, long: 4.770212173461914)
131
+ ]
132
+ end
133
+
134
+ end
135
+ end