flight 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,270 @@
1
+ require 'weighted_average'
2
+
3
+ module BrighterPlanet
4
+ module Flight
5
+ module CarbonModel
6
+ def self.included(base)
7
+ base.send :include, ::Leap::Subject
8
+ base.decide :emission, :with => :characteristics do
9
+ committee :emission do
10
+ quorum 'from fuel and passengers with coefficients',
11
+ :needs => [:fuel, :passengers, :seat_class_multiplier, :emission_factor,
12
+ :radiative_forcing_index, :freight_share, :date] do |characteristics, timeframe|
13
+ if timeframe.include? characteristics[:date]
14
+ #( kg fuel ) * ( kg CO2 / kg fuel ) = kg CO2
15
+ (characteristics[:fuel] / characteristics[:passengers] * characteristics[:seat_class_multiplier]) * characteristics[:emission_factor] * characteristics[:radiative_forcing_index] * (1 - characteristics[:freight_share])
16
+ else
17
+ 0
18
+ end
19
+ end
20
+
21
+ quorum 'default' do
22
+ raise "The emission committee's default quorum should never be called"
23
+ end
24
+ end
25
+
26
+ committee :fuel do # returns kg fuel
27
+ quorum 'from fuel per segment and emplanements and trips', :needs => [:fuel_per_segment, :emplanements_per_trip, :trips] do |characteristics|
28
+ characteristics[:fuel_per_segment] * characteristics[:emplanements_per_trip].to_f * characteristics[:trips].to_f
29
+ end
30
+ end
31
+
32
+ committee :fuel_per_segment do # returns kg fuel
33
+ quorum 'from adjusted distance and fuel use formula and emplanements and trips', :needs => [:adjusted_distance_per_segment, :fuel_use_coefficients, :endpoint_fuel] do |characteristics|
34
+ characteristics[:fuel_use_coefficients][:m3].to_f * characteristics[:adjusted_distance_per_segment].to_f ** 3 +
35
+ characteristics[:fuel_use_coefficients][:m2].to_f * characteristics[:adjusted_distance_per_segment].to_f ** 2 +
36
+ characteristics[:fuel_use_coefficients][:m1].to_f * characteristics[:adjusted_distance_per_segment].to_f +
37
+ characteristics[:endpoint_fuel].to_f
38
+ end
39
+ end
40
+
41
+ committee :adjusted_distance_per_segment do
42
+ quorum 'from adjusted distance and emplanements', :needs => [:adjusted_distance, :emplanements_per_trip] do |characteristics|
43
+ characteristics[:adjusted_distance] / characteristics[:emplanements_per_trip]
44
+ end
45
+ end
46
+
47
+ committee :endpoint_fuel do
48
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
49
+ characteristics[:aircraft].endpoint_fuel
50
+ end
51
+
52
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
53
+ characteristics[:aircraft_class].endpoint_fuel
54
+ end
55
+
56
+ quorum 'default' do
57
+ Aircraft.fallback.andand.endpoint_fuel
58
+ end
59
+ end
60
+
61
+ committee :fuel_use_coefficients do
62
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
63
+ characteristics[:aircraft].attributes.symbolize_keys.slice(:m1, :m2, :m3)
64
+ end
65
+
66
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
67
+ characteristics[:aircraft_class].attributes.symbolize_keys.slice(:m1, :m2, :m3)
68
+ end
69
+
70
+ quorum 'default' do
71
+ fallback = Aircraft.fallback
72
+ if fallback
73
+ fallback.attributes.symbolize_keys.slice(:m1, :m2, :m3)
74
+ end
75
+ end
76
+ end
77
+
78
+ committee :passengers do
79
+ quorum 'from seats and load factor', :needs => [:seats, :load_factor] do |characteristics|
80
+ (characteristics[:seats] * characteristics[:load_factor]).round
81
+ end
82
+ end
83
+
84
+ committee :seats do
85
+ # leaving this here to explain how someday we might lookup seat count based on both airline AND aircraft
86
+ #SE quorum 'from_airline_and_aircraft', :needs => [:airline, :aircraft] do |characteristics, timeframe|
87
+ #SE if aircraft = AirlineAircraft.memoized_find_by_airline_id_and_aircraft_id(characteristics[:airline].id, characteristics[:aircraft].id)
88
+ #SE aircraft.seats
89
+ #SE end
90
+ #SE end
91
+
92
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
93
+ characteristics[:aircraft].seats
94
+ end
95
+
96
+ quorum 'from seats estimate', :needs => :seats_estimate do |characteristics|
97
+ characteristics[:seats_estimate]
98
+ end
99
+
100
+ quorum 'from cohort', :needs => :cohort do |characteristics|
101
+ seats = characteristics[:cohort].weighted_average :seats, :weighted_by => :passengers
102
+ if seats.nil? or seats.zero?
103
+ nil
104
+ else
105
+ seats
106
+ end
107
+ end
108
+
109
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
110
+ characteristics[:aircraft_class].seats
111
+ end
112
+
113
+ quorum 'default' do
114
+ FlightSegment.fallback.andand.seats
115
+ end
116
+ end
117
+
118
+ committee :load_factor do
119
+ quorum 'from cohort', :needs => :cohort do |characteristics|
120
+ characteristics[:cohort].weighted_average(:load_factor, :weighted_by => :passengers)
121
+ end
122
+
123
+ quorum 'default' do
124
+ BrighterPlanet::Flight.flight_model.fallback.andand.load_factor
125
+ end
126
+ end
127
+
128
+ committee :adjusted_distance do # returns nautical miles
129
+ quorum 'from distance', :needs => [:distance, :emplanements_per_trip] do |characteristics|
130
+ route_inefficiency_factor = BrighterPlanet::Flight.flight_model.research(:route_inefficiency_factor)
131
+ dogleg_factor = BrighterPlanet::Flight.flight_model.research(:dogleg_factor)
132
+ characteristics[:distance] * route_inefficiency_factor * ( dogleg_factor ** (characteristics[:emplanements_per_trip] - 1) )
133
+ end
134
+ end
135
+
136
+ committee :distance do # returns nautical miles
137
+ quorum 'from airports', :needs => [:origin_airport, :destination_airport] do |characteristics|
138
+ if characteristics[:origin_airport].latitude and
139
+ characteristics[:origin_airport].longitude and
140
+ characteristics[:destination_airport].latitude and
141
+ characteristics[:destination_airport].longitude
142
+ characteristics[:origin_airport].distance_to(characteristics[:destination_airport], :units => :kms).kilometres.to :nautical_miles
143
+ end
144
+ end
145
+
146
+ quorum 'from distance estimate', :needs => :distance_estimate do |characteristics|
147
+ characteristics[:distance_estimate].kilometres.to :nautical_miles
148
+ end
149
+
150
+ quorum 'from distance class', :needs => :distance_class do |characteristics|
151
+ characteristics[:distance_class].distance.kilometres.to :nautical_miles
152
+ end
153
+
154
+ quorum 'from cohort', :needs => :cohort do |characteristics|
155
+ distance = characteristics[:cohort].weighted_average(:distance, :weighted_by => :passengers).to_f.kilometres.to(:nautical_miles)
156
+ distance > 0 ? distance : nil
157
+ end
158
+
159
+ quorum 'default' do
160
+ BrighterPlanet::Flight.flight_model.fallback.distance_estimate.kilometres.to :nautical_miles
161
+ end
162
+ end
163
+
164
+ committee :emplanements_per_trip do # per trip
165
+ quorum 'default' do
166
+ BrighterPlanet::Flight.flight_model.fallback.emplanements_per_trip_before_type_cast
167
+ end
168
+ end
169
+
170
+ committee :radiative_forcing_index do
171
+ quorum 'from fuel type', :needs => :fuel_type do |characteristics|
172
+ characteristics[:fuel_type].radiative_forcing_index
173
+ end
174
+ end
175
+
176
+ committee :emission_factor do # returns kg CO2 / kg fuel
177
+ quorum 'from fuel type', :needs => :fuel_type do |characteristics|
178
+ #( kg CO2 / litres fuel ) * ( litres fuel / kg fuel )
179
+ characteristics[:fuel_type].emission_factor * ( 1 / characteristics[:fuel_type].density).gallons.to(:litres)
180
+ end
181
+ end
182
+
183
+ committee :fuel_type do
184
+ quorum 'default' do
185
+ FlightFuelType.fallback
186
+ end
187
+ end
188
+
189
+ committee :freight_share do
190
+ quorum 'from cohort', :needs => :cohort do |characteristics|
191
+ characteristics[:cohort].weighted_average(:freight_share, :weighted_by => :passengers)
192
+ end
193
+
194
+ quorum 'default' do
195
+ FlightSegment.fallback.andand.freight_share
196
+ end
197
+ end
198
+
199
+ committee :trips do
200
+ quorum 'default' do
201
+ BrighterPlanet::Flight.flight_model.fallback.andand.trips_before_type_cast
202
+ end
203
+ end
204
+
205
+ committee :domesticity do
206
+ quorum 'from airports', :needs => [:origin_airport, :destination_airport] do |characteristics|
207
+ if [characteristics[:origin_airport], characteristics[:destination_airport]].all?(&:united_states?)
208
+ FlightDomesticity.find_by_name('domestic')
209
+ elsif [characteristics[:origin_airport], characteristics[:destination_airport]].any?(&:united_states?)
210
+ FlightDomesticity.find_by_name('international')
211
+ end
212
+ end
213
+
214
+ quorum 'from origin', :needs => :origin_airport do |characteristics|
215
+ if characteristics[:origin_airport].all_flights_from_here_domestic?
216
+ FlightDomesticity.find_by_name('domestic')
217
+ end
218
+ end
219
+
220
+ quorum 'from destination', :needs => :destination_airport do |characteristics|
221
+ if characteristics[:destination_airport].all_flights_to_here_domestic?
222
+ FlightDomesticity.find_by_name('domestic')
223
+ end
224
+ end
225
+
226
+ quorum 'from airline', :needs => :airline do |characteristics|
227
+ if characteristics[:airline].all_flights_domestic?
228
+ FlightDomesticity.find_by_name('domestic')
229
+ end
230
+ end
231
+ end
232
+
233
+ committee :seat_class_multiplier do
234
+ quorum 'from seat class', :needs => :seat_class do |characteristics|
235
+ characteristics[:seat_class].multiplier
236
+ end
237
+
238
+ quorum 'default' do
239
+ FlightSeatClass.fallback.andand.multiplier
240
+ end
241
+ end
242
+
243
+ committee :date do
244
+ quorum 'from creation date', :needs => :creation_date do |characteristics|
245
+ characteristics[:creation_date]
246
+ end
247
+
248
+ quorum 'from timeframe' do |characteristics, timeframe|
249
+ timeframe.andand.from
250
+ end
251
+ end
252
+
253
+ committee :cohort do
254
+ quorum 'from t100', :appreciates => FlightSegment::INPUT_CHARACTERISTICS do |characteristics|
255
+ needed_characteristics = characteristics
256
+ needed_characteristics.
257
+ reject! { |k,v| !FlightSegment::INPUT_CHARACTERISTICS.include?(k) }
258
+ cohort = FlightSegment.big_cohort needed_characteristics
259
+ if cohort.any?
260
+ cohort
261
+ else
262
+ nil
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,31 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Characterization
4
+ def self.included(base)
5
+ base.characterize do
6
+ has :date, :trumps => :year
7
+ has :year
8
+ has :time_of_day
9
+ has :origin_airport do |origin_airport|
10
+ origin_airport.reveals :destination_airport,
11
+ :trumps => [:distance_class, :domesticity, :distance_estimate]
12
+ end
13
+ has :distance_class
14
+ has :distance_estimate, :trumps => :distance_class, :measures => :length, :precision => 0
15
+ has :domesticity
16
+ has :airline
17
+ has :trips
18
+ has :emplanements_per_trip
19
+ has :seat_class
20
+ has :load_factor, :measures => :percentage
21
+ has :seats_estimate, :range => 1..500
22
+ has :aircraft_class, :trumps => [:propulsion, :fuel_type]
23
+ has :aircraft, :trumps => [:propulsion, :aircraft_class, :seats_estimate, :fuel_type]
24
+ has :propulsion, :trumps => :fuel_type
25
+
26
+ has :creation_date, :hidden => true
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Data
4
+ def self.included(base)
5
+ base.data_miner do
6
+ schema do
7
+ string 'origin_airport_id'
8
+ string 'destination_airport_id'
9
+ integer 'trips'
10
+ integer 'emplanements_per_trip'
11
+ float 'distance_estimate'
12
+ string 'distance_class_id'
13
+ string 'aircraft_id'
14
+ string 'aircraft_class_id'
15
+ string 'propulsion_id'
16
+ string 'fuel_type_id'
17
+ string 'airline_id'
18
+ string 'seat_class_id'
19
+ integer 'seats_estimate'
20
+ float 'load_factor'
21
+ string 'domesticity_id'
22
+ date 'date'
23
+ integer 'year'
24
+ time 'time_of_day'
25
+ end
26
+
27
+ process "pull dependencies" do
28
+ run_data_miner_on_belongs_to_associations
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Summarization
4
+ def self.included(base)
5
+ base.summarize do |has|
6
+ has.adjective 'one-way', :if => lambda { |flight| flight.trips == 1 }
7
+ has.adjective 'round-trip', :if => lambda { |flight| flight.trips == 1 }
8
+ has.adjective 'nonstop', :if => lambda { |flight| flight.emplanements_per_trip == 1 }
9
+ has.identity 'flight'
10
+ has.modifier lambda { |flight| "from #{flight.origin_airport.name}" }, :if => :origin_airport
11
+ has.modifier lambda { |flight| "to #{flight.destination_airport.name}" }, :if => :destination_airport
12
+ has.modifier lambda { |flight| "on a #{flight.vehicle}" }, :if => :vehicle
13
+ has.modifier lambda { |flight| "on #{flight.date.to_formatted_s(:archive)}"}, :if => :date
14
+ has.verb :take
15
+ has.aspect :perfect
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ class Aircraft < ActiveRecord::Base
2
+ set_table_name 'aircraft'
3
+ set_primary_key :icao_code
4
+
5
+ belongs_to :aircraft_class, :foreign_key => 'brighter_planet_aircraft_class_code'
6
+ belongs_to :manufacturer, :foreign_key => 'manufacturer_name', :class_name => 'AircraftManufacturer'
7
+ has_many :segments, :foreign_key => 'bts_aircraft_type_code', :class_name => "FlightSegment", :primary_key => 'bts_aircraft_type_code'
8
+
9
+ falls_back_on :m3 => lambda { weighted_average(:m3, :weighted_by => [:segments, :passengers]) }, # 9.73423082858437e-08 r7110: 8.6540464368905e-8 r6972: 8.37e-8
10
+ :m2 => lambda { weighted_average(:m2, :weighted_by => [:segments, :passengers]) }, # -0.000134350543484608 r7110: -0.00015337661447817 r6972: -4.09e-5
11
+ :m1 => lambda { weighted_average(:m1, :weighted_by => [:segments, :passengers]) }, # 6.7728101555467 r7110: 4.7781966869412 r6972: 7.85
12
+ :endpoint_fuel => lambda { weighted_average(:endpoint_fuel, :weighted_by => [:segments, :passengers]) }, # 1527.81790006167 r7110: 1065.3476555284 r6972: 1.72e3
13
+ :seats => lambda { weighted_average(:seats, :weighted_by => [:segments, :passengers]) } # 62.1741
14
+
15
+ class << self
16
+ def loose_search_columns
17
+ @_loose_search_columns ||= [primary_key, :manufacturer_name, :name]
18
+ end
19
+
20
+ # search by name
21
+ def loose_right_reader
22
+ @_loose_right_reader ||= lambda { |record| record[1,2].join ' ' }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ class AircraftClass < ActiveRecord::Base
2
+ set_primary_key :brighter_planet_aircraft_class_code
3
+
4
+ has_many :aircraft, :foreign_key => 'brighter_planet_aircraft_class_code'
5
+ # has_many :airline_aircraft_seat_classes, :through => :aircraft
6
+ end
@@ -0,0 +1,5 @@
1
+ class AircraftManufacturer < ActiveRecord::Base
2
+ set_primary_key :name
3
+
4
+ has_many :aircraft, :foreign_key => 'manufacturer_name'
5
+ end
@@ -0,0 +1,24 @@
1
+ class Airline < ActiveRecord::Base
2
+ set_primary_key :iata_code
3
+
4
+ # has_many :airline_aircraft, :class_name => 'AirlineAircraft'
5
+ # has_many :seat_classes, :class_name => 'AirlineSeatClass'
6
+ has_many :segments, :class_name => "FlightSegment", :foreign_key => 'airline_iata_code'
7
+ # has_many :airline_aircraft_seat_classes, :class_name => 'AirlineAircraftSeatClass'
8
+
9
+
10
+ class << self
11
+ def loose_search_columns
12
+ @_loose_search_columns ||= [primary_key, :name]
13
+ end
14
+
15
+ # search by name
16
+ def loose_right_reader
17
+ @_loose_right_reader ||= lambda { |record| record[1] }
18
+ end
19
+ end
20
+
21
+ def all_flights_domestic?
22
+ !international?
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ class Airport < ActiveRecord::Base
2
+ set_primary_key :iata_code
3
+
4
+ acts_as_mappable :default_units => :nms,
5
+ :lat_column_name => :latitude,
6
+ :lng_column_name => :longitude
7
+
8
+ class << self
9
+ def loose_search_columns
10
+ @_loose_search_columns ||= [primary_key, :city]
11
+ end
12
+
13
+ # search by name
14
+ def loose_right_reader
15
+ @_loose_right_reader ||= lambda { |record| record[1] }
16
+ end
17
+ end
18
+
19
+ def name_and_location
20
+ [ name.to_s.titleize, city, country.andand.name ].select(&:present?).join ', '
21
+ end
22
+
23
+ # --------------------------------
24
+ # virtual has_many association
25
+ # has_many :segments won't work because there's no general way to specify the correct conditions
26
+ # even if you get clever with it, like
27
+ # has_many :segments,
28
+ # :class_name => 'FlightSegment',
29
+ # :foreign_key => 'origin_airport_id',
30
+ # :conditions => 'flight_segments.destination_airport_id = #{id}'
31
+ # you get queries like "`flight_segments`.origin_airport_id = 3654 AND (flight_segments.destination_airport_id = 3654))"
32
+ # in which you notice the AND which must be an OR
33
+ # and you can't just do finder_sql, because that breaks any other :select
34
+ def segments
35
+ FlightSegment.scoped :conditions => ['origin_airport_id = ? OR destination_airport_id = ?', id, id]
36
+ end
37
+ # --------------------------------
38
+
39
+ belongs_to :country, :foreign_key => 'country_iso_3166_code'
40
+
41
+ def all_flights_from_here_domestic?
42
+ !international_origin?
43
+ end
44
+
45
+ def all_flights_to_here_domestic?
46
+ !international_destination?
47
+ end
48
+
49
+ def united_states?
50
+ country == Country.united_states
51
+ end
52
+ end