My “Worst” Layovers

Flying out of a smaller city like Dayton, I’m used to having flight layovers on the way to nearly everywhere I travel. While any layover is going to lengthen a trip, one of the most common complaints I hear from traveling companions is when a layover forces them to fly east to go west, or vice versa.

A map of flights from DAY to IAD to TUL.

Traveling east (DAY–IAD) to go west (TUL).

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

I started thinking about a way to quantify how bad a layover was, and ultimately decided that it would be best to compare the sum of the (great circle) distances for each of the flights flown compared to the (great circle) distance of a direct flight from the origin to the destination:

$${ratio}_{layover} = \dfrac{distance_1+distance_2+\ldots+distance_n}{distance_\text{direct}}$$

This would give me a ratio of how much further I flew than I needed to, where a higher ratio would mean a worse layover. A ratio of 2 would mean I flew twice as far as I needed to, a ratio of 3 would mean three times as far, and so on. A ratio of 1 would mean a layover didn’t add any extra distance at all.

My Worst 5 Layovers

Since I keep track of all of my flight data, I can use this ratio to determine my worst layovers.

#5 Worst: Nashville–Charlotte–Dayton

A map of flights from BNA to CLT to DAY.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

697 mi flown / 293 mi direct
Ratio: 2.379

A lot of my bad layovers come from trips that are just on the threshold where either driving or flying could make sense (for me, about a six hour drive). Because these are some of the shortest direct distances I fly, any deviation in the layover tends to greatly increase the length of the trip. In this case, the route was about 2.3 times longer than a direct flight would have been.

#4 Worst: St. Louis–Charlotte–Dayton (both directions)

A map of flights from STL to CLT to DAY.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

944 mi flown / 338 mi direct
Ratio: 2.793

Similarly, St. Louis is right on the drive/fly threshold for me.

There used to be a direct flight between Dayton and St. Louis back when American Airlines was still operating St. Louis as a hub it inherited from TWA, but now it takes a layover to get there. Usually I can at least go through Chicago O’Hare which is more direct (ratio of 1.467), but occasionally I end up having to fly through Charlotte to get a flight at the right time of day.

#3 Worst: Dayton–Dallas/Fort Worth–Boston

A map of flights from DAY to DFW to BOS.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

2419 mi flown / 707 mi direct
Ratio: 3.421

This is the one that I thought would be my worst layover. I got this trip for free with frequent flier miles, so I wasn’t going to complain too much about the routing, but I’ve always thought this was a pretty ridiculous-looking map.

#2 Worst: Milwaukee–Atlanta–Dayton

A map of flights from MKE to ATL to DAY.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

1183 mi flown / 283 mi direct
Ratio: 3.898

All I can guess is that it was probably the cheapest flight available when I booked it, and I wasn’t a very experienced traveler at the time.

#1 Worst: Des Moines–Houston–Wichita

A map of flights from DSM to IAH to ICT.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

1346 mi flown / 335 mi direct
Ratio: 4.018

This is one of my few short trips that didn’t start at home. I had a work trip where I had to be in Des Moines for the first half of the week, and Wichita the second half. Again, it’s about a six hour drive between the two cities, but with as out of the way as this layover turned out to be (more than quadrupling my distance traveled!), I might have been better off driving.

My Best 5 Layovers

#5 Best: Chicago–Toronto–Munich

A map of flights from ORD to YYZ to MUC.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

4561 mi flown / 4517 mi direct
Ratio: 1.010

While there are nonstop flights available between Chicago and Munich, I booked this route on frequent flier miles and had to take a layover to do so. That said, it only added a percent to the length of the trip (and at least made the transatlantic flight slightly shorter), so it worked out fine.

#4 Best: Dayton–Denver–Burbank

A map of flights from DAY to DEN to BUR.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

1930 mi flown / 1911 mi direct
Ratio: 1.010

Dayton doesn’t have any direct flights to west coast airports (in fact, Denver is the longest direct flight from Dayton), so this routing was pretty decent to get to Burbank.

#3 Best: Charleston–Charlotte–Dayton

A map of flights from CHS to CLT to DAY.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

538 mi flown / 536 mi direct
Ratio: 1.004

Normally my job had me flying United when I went to Charleston, so I had a lot of layovers at Washington Dulles. However, my very first return flight from Charleston was right after United had merged their reservation system with Continental. They were having a lot of issues and my flight got cancelled, so United ended up putting me on a US Airways flight through Charlotte, which was a better layover anyway.

#2 Best: Dayton–Chicago–Seattle (both directions)

A map of flights from DAY to ORD to SEA.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

1955 mi flown / 1952 mi direct
Ratio: 1.002

This route was what I expected my best layover to be, and it looks like I was only one place off. The stop in Chicago only adds two tenths of a percent to the length of this route.

#1 Best: Chicago–Cleveland–New York

A map of flights from ORD to CLE to JFK.

Map generated by Paul Bogard using the Great Circle Mapper - copyright © Karl L. Swartz

738 mi flown / 738 mi direct
Ratio: 1.000

So while a trip with a layover is still going to take longer than a direct flight, this is about the best layover you can get: any increase in distance for the layover is within the rounding error, and the stop didn’t add a single extra mile.

Interestingly enough, this trip section was part of the same trip that had my second-worst layover of MilwaukeeAtlanta–Dayton, shown above.

Methodology

My flight log (written in Ruby on Rails) has a table of flights; each flight has a trip_id and a trip_section number within that trip. Since layovers are going to be contained within trip sections, I needed to first determine every unique trip_id and trip_section combinations in my flight log:

Flight.all.map{|f| [f.trip_id, f.trip_section]}.uniq

Then I used that to create an array of trip sections, each entity of which contained an array of pairs of airport codes (for example, [["DAY","CLT"],["CLT","STL"]]):

.map{|ts| Flight.where(trip_id: ts.first, trip_section: ts.last)
  .order(:departure_utc)
  .map{|f| [f.origin_airport.iata_code, f.destination_airport.iata_code]}
}

Once I had that, I used uniq to remove duplicate routes. Since there was no point in evaluating direct flights (e.g., routes with just a single flight), I also used a select block to keep only routes that had more than one flight:

.uniq.select{|f| f.count > 1}

So now that I had a collection of trip sections with layovers, I had to calculate their total distance, and the direct distance between the first flight’s origin and the last flight’s destination.

Every airport in my flight log has a latitude and longitude stored, and my flight log already has a Route.distance_by_iata(iata1, iata2) method to find the great circle distance between two airport codes (using the haversine formula).

To get the total trip section route distance flown, I used a map command to create an array of trip distances, and a reduce command to sum them (assuming ts is the array of flight airport code pairs in a trip section):

rd = ts.map{|f| Route.distance_by_iata(f.first, f.last)}.reduce(0, :+)

Best distance (direct flight distance) is easier, since I just need to run the distance calculation on the first flight’s first airport, and the last flight’s last airport:

bd = Route.distance_by_iata(ts.first.first, ts.last.last)

So combining these, we can use a map on the collection of trip sections to create an array of hashes of trip section routes, distances, best distances, and ratios:

.map{|ts| rd = ts.map{|f|
  Route.distance_by_iata(f.first, f.last)}.reduce(0, :+);
  bd = Route.distance_by_iata(ts.first.first, ts.last.last);
  {
    route: ts.map{|f| f.first}.push(ts.last.last).join("-"),
    route_distance: rd,
    best_distance: bd,
    ratio: (rd.to_f/bd.to_f).round(3)
  }
}

And sort it by ratio descending:

.sort_by{|f| -f[:ratio]}

Combining these all into a single statement:

output = Flight.all.map{|f| [f.trip_id, f.trip_section]}
  .uniq
  .map{|ts| Flight.where(trip_id: ts.first, trip_section: ts.last)
    .order(:departure_utc)
    .map{|f| [f.origin_airport.iata_code, f.destination_airport.iata_code]}
  }
  .uniq
  .select{|f| f.count > 1}
  .map{|ts|
    rd = ts.map{|f| Route.distance_by_iata(f.first, f.last)}.reduce(0, :+);
    bd = Route.distance_by_iata(ts.first.first, ts.last.last);
    {
      route: ts.map{|f| f.first}.push(ts.last.last).join("-"),
      route_distance: rd,
      best_distance: bd,
      ratio: (rd.to_f/bd.to_f).round(3)
    }
  }
  .sort_by{|f| -f[:ratio]}

Running it on my flight log provided me my results:

Terminal showing hashes of routes and their distances, best distances, and ratios.

And for ease of comparison, I decided to convert it into CSV-formatted output so I could import it into Excel:

output.map{|f| puts f[:route] + "," + f[:route_distance].to_s + "," +
  f[:best_distance].to_s + "," + f[:ratio].to_s + "\n"
}
Terminal showing the same data as before, but in CSV format.

With that, I had all the information I needed to create my 5 best and 5 worst layovers list.

Tags:
Airports: