Skip to content

Instantly share code, notes, and snippets.

@SeanDS
Last active October 8, 2019 08:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SeanDS/4e7c8f532c464f64a5a6817c641341ca to your computer and use it in GitHub Desktop.
Save SeanDS/4e7c8f532c464f64a5a6817c641341ca to your computer and use it in GitHub Desktop.
Carbon emissions calculation
# Quick and dirty carbon emissions calculation.
#
# See https://attackllama.com/carbon/.
#
# Requires Python 3.6+.
# Non-core dependencies: requests, numpy, pandas, matplotlib, pytablewriter.
#
# Note that the input text file "flights.txt" has a "distance" column. This is
# obtained manually using online flight distance calculators, and is simply for
# statistics. If you don't want to use this data, set each row in this column
# to zero.
#
#
# Sean Leavey
# <github@attackllama.com>
from collections import defaultdict
import csv
import requests
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pytablewriter import RstGridTableWriter
API_KEY = "" # get from goclimateneutral.org
API_BASE = 'https://api.goclimateneutral.org'
# https://trees.org/carboncalculator/, 15.6943 kg/year, assumes 40 year life
CO2_KG_ABSORBED_PER_TREE = 15.6943 * 40
def get_carbon():
co2total = 0
nflights = 0
dtotal = 0
fdata = []
headers = ["Year", "From", "To", "Via 1", "Via 2", "Is Return", "Distance (km)", "Footprint (CO2 kg)", "Notes"]
with open('flights.txt') as csvfile:
reader = csv.reader(csvfile, delimiter='\t')
# Skip header.
next(reader)
for row in reader:
(year, airport_from, airport_to, airport_via1, airport_via2, is_return,
distance, notes) = row
# Fix types.
is_return = True if is_return.lower() == "yes" else False
distance = float(distance)
nflight = 1
payload = {
'segments': {},
'cabin_class': 'economy',
'currencies[]': 'EUR',
}
if airport_via1 != "":
nflight += 1
payload["segments[0][origin]"] = airport_from
payload["segments[0][destination]"] = airport_via1
if airport_via2 != "":
nflight += 1
payload["segments[1][origin]"] = airport_via1
payload["segments[1][destination]"] = airport_via2
payload["segments[2][origin]"] = airport_via2
payload["segments[2][destination]"] = airport_to
else:
payload["segments[1][origin]"] = airport_via1
payload["segments[1][destination]"] = airport_to
else:
payload["segments[0][origin]"] = airport_from
payload["segments[0][destination]"] = airport_to
# Get data.
response = requests.get(API_BASE + '/v1/flight_footprint',
params=payload,
auth=(API_KEY, ''))
if response.status_code != 200:
raise Exception(f"invalid response: {response.text}")
data = response.json()
scale = 2 if is_return else 1
footprint = scale * float(data["footprint"])
co2total += footprint
if is_return:
nflight *= 2
nflights += nflight
dtotal += distance
# Create new data
fdata.append(
(year, airport_from, airport_to, airport_via1, airport_via2,
is_return, distance, footprint, notes)
)
with open('carbon.csv', 'w') as csvfile:
writer = csv.writer(csvfile, delimiter='\t', quotechar='|',
quoting=csv.QUOTE_MINIMAL)
writer.writerow(headers)
writer.writerows(fdata)
print(f"Number of flights: {nflights}")
print(f"Total distance: {dtotal} km")
print(f"Total CO2: {co2total} kg")
def load_carbon():
return pd.read_csv('carbon.csv', sep='\t')
def to_rst_table():
table = load_carbon()
table = table[["Year", "From", "To", "Is Return", "Distance (km)", "Footprint (CO2 kg)"]]
writer = RstGridTableWriter()
writer.from_dataframe(table)
writer.write_table()
def plot_timeline():
data = load_carbon()
subdata = data[["Year", "Distance (km)", "Footprint (CO2 kg)"]]
subsum = subdata.groupby(by=["Year"]).sum().cumsum()
distance = subsum["Distance (km)"]
earths = distance / 40075.017
earths.rename("Earths")
footprint = subsum["Footprint (CO2 kg)"]
footprint_tonnes = footprint / 1000
footprint_tonnes.rename("Footprint (CO2 tonnes)")
trees = footprint / CO2_KG_ABSORBED_PER_TREE
ax1 = distance.plot()
ax1.ticklabel_format(axis='y', style='sci', scilimits=(3,3))
lines1, labels1 = ax1.get_legend_handles_labels()
ax2 = ax1.twinx()
earths.plot(ax=ax2, alpha=0)
ax2.spines['left'].set_position(('axes', -0.22))
ax2.spines["left"].set_visible(True)
ax2.yaxis.set_label_position('left')
ax2.yaxis.set_ticks_position('left')
ax3 = ax1.twinx()
footprint_tonnes.plot(ax=ax3, color="orange")
ax3.spines['right'].set_position(('axes', 1.0))
lines2, labels2 = ax3.get_legend_handles_labels()
ax4 = ax1.twinx()
trees.plot(ax=ax4, alpha=0)
ax4.spines['right'].set_position(('axes', 1.2))
ax1.grid(True)
fig = plt.gcf()
ax1.legend(lines1+lines2, ["Distance", "Footprint"])
ax1.set_ylabel('Distance (km)')
ax2.set_ylabel('Earth circumnavigations')
ax3.set_ylabel('Footprint (tonnes CO2)')
ax4.set_ylabel('Required trees')
ax1.set_title("Lifetime flight footprint")
#fig.autofmt_xdate()
fig.tight_layout()
fig.savefig("carbon.png")
plt.show()
Year From To Via 1 Via 2 Return? Distance (km) Notes
1991 GLA YYZ Yes 10600
1993 GLA YYZ Yes 10600
1995 GLA YYZ Yes 10600
1998 GLA BJV Yes 6300
1998 GLA YYZ Yes 10600
1998 YYZ YFC Yes 2100
1999 GLA JSI Yes 5500
2000 GLA LHR Yes 1100
2000 GLA YYZ Yes 10600
2001 GLA BCN Yes 3400
2002 GLA GVA Yes 2600
2003 GLA YYZ Yes 10600
2006 MAN BVA Yes 1800
2006 EDI FNC Yes 5500
2007 GLA YYZ Yes 10600
2009 GLA FNC Yes 5500
2010 GLA YYC Yes 13000
2010 EDI MXP LHR No 1500
2010 AMS EDI No 700
2011 GLA MAD Yes 3400
2011 EDI MUC Yes 2700
2012 GLA LHR Yes 1100
2012 HAJ GLA LHR Yes 2500
2013 GLA FNC LHR Yes 6100
2013 GLA WAW Yes 3300
2013 EDI MUC Yes 2700
2013 GLA HAJ LHR No 1250
2013 HAJ GLA LHR Yes 2500
2014 HAJ MUC Yes 1000
2014 HAJ GLA LHR No 1250
2014 GLA SFO BGR No 9000
2014 LAX MAN PHL No 9400
2014 GLA HAJ LHR Yes 2500
2014 GLA LYS AMS Yes 2900
2015 GLA HAJ LHR Yes 2500
2015 GLA ANC LHR SEA Yes 21200
2015 EDI BRE No 800
2015 FRA GLA No 1100
2015 GLA HAJ LHR Yes 2500
2016 GLA BLQ No 1700
2016 FLR GLA No 1700
2016 EDI FNC LIS No 2900
2016 FNC EDI No 2800
2016 GLA LAX LHR Yes 18600
2016 GLA PMI Yes 3800
2016 GLA IAD LHR No 6500
2016 JFK GLA LHR No 6100
2017 GLA XRY MAD Yes 4400
2017 GLA FRA No 1100
2017 TXL GLA No 1200
2017 GLA HAJ LHR Yes 2500
2017 GLA HAJ LHR No 1250
2017 HAJ GLA LHR Yes 2500
2017 HAJ GLA LHR Yes 2500
2018 HAJ LHR Yes 1400
2018 HAJ FNC Yes 6100
2018 HAJ GLA LHR Yes 2500
2018 HAJ PMI Yes 3100
2018 HAJ GLA LHR Yes 2500
2019 HAJ GLA LHR Yes 2500
2019 HAJ FCO FRA No 1200
2019 FCO HAJ MUC No 1200
2019 HAJ GLA LHR Yes 2500
2019 HAJ LHR Yes 1400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment