Skip to content

Commit

Permalink
Added example with all DK
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgecarleitao committed Nov 22, 2023
1 parent 6b92049 commit e730ccb
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 77 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[dependencies]
# process JSON from https
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = { version = "1.0", default_features = false }

# perform requests to the internet
Expand Down
24 changes: 24 additions & 0 deletions examples/dk_jets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Use of Danish private jets

{from_date} to {to_date}, Danish private jets emitted
{emissions_tons.claim} tons of CO2e[^5], the equivalent of what **{dane_years.claim} Danes**
emit in a year[^4].

Of these,
* {number_of_legs_less_300km} trips were less than 300 km and could have been replaced by
a limusine or first class train ticket, which would have emitted ~30-50x less CO2.
* {number_of_legs_more_300km} trips could have been replaced by
a first class ticket on a commercial aircraft, which would have emitted
~{ratio_commercial_300km}x less CO2[^5].

Billionaires and companies alike are incapable of regulating their emissions,
recklessly destroying the common good.
Ban private jets now and until they emit what equivalent means of transportation would emit.

## References

[^2]: {number_of_legs.source} - retrieved on {number_of_legs.date}
[^4]: {dane_years.source} - retrieved on {dane_years.date}
[^5]: {emissions_tons.source} - retrieved on {emissions_tons.date}

Copyright Jorge Leitão, released under [CC0](https://creativecommons.org/public-domain/cc0/) - No Rights Reserved.
198 changes: 198 additions & 0 deletions examples/dk_jets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
use std::{collections::HashMap, error::Error, sync::Arc};

use clap::Parser;
use futures::{StreamExt, TryStreamExt};
use simple_logger::SimpleLogger;

use flights::{emissions, load_aircraft_types, load_aircrafts, Class, Fact, Position};
use time::macros::date;

fn render(context: &Context) -> Result<(), Box<dyn Error>> {
let path = "all_dk_jets.md";

let template = std::fs::read_to_string("examples/dk_jets.md")?;

let mut tt = tinytemplate::TinyTemplate::new();
tt.set_default_formatter(&tinytemplate::format_unescaped);
tt.add_template("t", &template)?;

let rendered = tt.render("t", context)?;

log::info!("Story written to {path}");
std::fs::write(path, rendered)?;
Ok(())
}

#[derive(serde::Serialize)]
pub struct Context {
pub from_date: String,
pub to_date: String,
pub number_of_legs: Fact<usize>,
pub emissions_tons: Fact<usize>,
pub dane_years: Fact<String>,
pub number_of_legs_less_300km: usize,
pub number_of_legs_more_300km: usize,
pub ratio_commercial_300km: String,
}

#[derive(clap::ValueEnum, Debug, Clone)]
enum Backend {
Disk,
Azure,
}

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// The Azure token
#[arg(short, long)]
azure_sas_token: Option<String>,
#[arg(short, long, value_enum, default_value_t=Backend::Azure)]
backend: Backend,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
SimpleLogger::new()
.with_level(log::LevelFilter::Info)
.init()
.unwrap();

let cli = Cli::parse();

// optionally initialize Azure client
let client = match (cli.backend, cli.azure_sas_token) {
(Backend::Disk, None) => None,
(Backend::Azure, None) => Some(flights::fs_azure::initialize_anonymous(
"privatejets",
"data",
)),
(_, Some(token)) => Some(flights::fs_azure::initialize_sas(
&token,
"privatejets",
"data",
)?),
};

// load datasets to memory
let aircrafts = load_aircrafts(client.as_ref()).await?;
let types = load_aircraft_types()?;

let dk_private_jets = aircrafts
.into_iter()
// is private jet
.filter(|(_, a)| types.contains_key(&a.model))
// is from DK
.filter(|(a, _)| a.starts_with("OY-"))
.collect::<HashMap<_, _>>();

let to = time::OffsetDateTime::now_utc().date() - time::Duration::days(1);
let from = date!(2021 - 01 - 01);

let from_date = from.to_string();
let to_date = to.to_string();

let dates = flights::DateIter {
from,
to,
increment: time::Duration::days(1),
};

let iter = dates
.map(|date| {
let client = client.as_ref();
dk_private_jets
.iter()
.map(move |(_, a)| flights::positions(&a.icao_number, date.clone(), 1000.0, client))
})
.flatten();

let positions = futures::stream::iter(iter)
// limit to 5 concurrent tasks
.buffer_unordered(100)
.try_collect::<Vec<_>>()
.await?;
let positions = positions.into_iter().flatten().collect::<Vec<_>>();

// group by aircraft
let mut positions = positions.into_iter().fold(
HashMap::<Arc<str>, Vec<Position>>::default(),
|mut acc, v| {
acc.entry(v.icao().clone())
.and_modify(|positions| positions.push(v.clone()))
.or_insert_with(|| vec![v]);
acc
},
);
// sort positions by datetime
positions.iter_mut().for_each(|(_, positions)| {
positions.sort_unstable_by_key(|x| x.datetime());
});

// compute legs
let legs = positions
.into_iter()
.map(|(icao, positions)| (icao, flights::real_legs(positions.into_iter())))
.collect::<HashMap<_, _>>();

let number_of_legs = Fact {
claim: legs.iter().map(|(_, legs)| legs.len()).sum::<usize>(),
source: format!("[adsbexchange.com](https://globe.adsbexchange.com) between {from_date} and {to_date} and all aircraft whose tail number starts with OY-"),
date: to.to_string()
};

let commercial_to_private_ratio = 10.0;
let commercial_emissions_tons = legs
.iter()
.map(|(_, legs)| {
legs.iter()
.map(|leg| emissions(leg.from.pos(), leg.to.pos(), Class::First) / 1000.0)
.sum::<f64>()
})
.sum::<f64>();
let emissions_tons = Fact {
claim: (commercial_emissions_tons * commercial_to_private_ratio) as usize,
source: format!("Commercial flights would have emitted {commercial_emissions_tons:.1} tons of CO2e (based on [myclimate.org](https://www.myclimate.org/en/information/about-myclimate/downloads/flight-emission-calculator/) - retrieved on 2023-10-19). Private jets emit 5-14x times. 10x was used based on [transportenvironment.org](https://www.transportenvironment.org/discover/private-jets-can-the-super-rich-supercharge-zero-emission-aviation/)"),
date: "2023-10-05, from 2021-05-27".to_string(),
};

let short_legs = legs
.iter()
.map(|(_, legs)| legs.iter().filter(|leg| leg.distance() < 300.0).count())
.sum::<usize>();
let long_legs = legs
.iter()
.map(|(_, legs)| legs.iter().filter(|leg| leg.distance() >= 300.0).count())
.sum::<usize>();

let dane_emissions_tons = Fact {
claim: 5.1,
source: "A dane emitted 5.1 t CO2/person/year in 2019 according to [work bank data](https://ourworldindata.org/co2/country/denmark).".to_string(),
date: "2023-10-08".to_string(),
};

let dane_years = format!(
"{:.0}",
emissions_tons.claim as f32 / dane_emissions_tons.claim as f32
);
let dane_years = Fact {
claim: dane_years,
source: "https://ourworldindata.org/co2/country/denmark Denmark emits 5.1 t CO2/person/year in 2019.".to_string(),
date: "2023-10-08".to_string(),
};

let context = Context {
from_date,
to_date,
number_of_legs,
emissions_tons,
dane_years,
number_of_legs_less_300km: short_legs,
number_of_legs_more_300km: long_legs,
ratio_commercial_300km: format!("{:.0}", commercial_to_private_ratio),
};

render(&context)?;

Ok(())
}
15 changes: 3 additions & 12 deletions examples/period.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
let aircrafts = load_aircrafts(client.as_ref()).await?;

let to = time::OffsetDateTime::now_utc().date() - time::Duration::days(1);
let from = to - time::Duration::days(90);
let from = to - time::Duration::days(365 * 3);

let tail_number = "OY-GFS";
let tail_number = "OY-DBS";
let aircraft = aircrafts
.get(tail_number)
.ok_or_else(|| Into::<Box<dyn Error>>::into("Aircraft ICAO number not found"))?
Expand Down Expand Up @@ -118,16 +118,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let mut positions = positions.into_iter().flatten().collect::<Vec<_>>();
positions.sort_unstable_by_key(|x| x.datetime());

let legs = flights::legs(positions.into_iter());
let legs = legs
.into_iter()
// ignore legs that are too fast, as they are likely noise
.filter(|leg| leg.duration() > time::Duration::minutes(5))
// ignore legs that are too short, as they are likely noise
.filter(|leg| leg.distance() > 3.0)
// ignore legs that are too low, as they are likely noise
.filter(|leg| leg.maximum_altitude > 1000.0)
.collect::<Vec<_>>();
let legs = flights::real_legs(positions.into_iter());
log::info!("number_of_legs: {}", legs.len());
for leg in &legs {
log::info!(
Expand Down
8 changes: 4 additions & 4 deletions src/aircraft_db.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// Contains the implementation to extract the database of all aircrafts available in ADS-B exchange
use std::collections::HashMap;
use std::error::Error;
/// Contains the implementation to extract the database of all aircrafts available in ADS-B exchange
use std::{collections::HashMap, sync::Arc};

use async_recursion::async_recursion;
use reqwest;
Expand All @@ -16,7 +16,7 @@ pub type Aircrafts = HashMap<String, Aircraft>;
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Aircraft {
/// The ICAO number of the aircraft (e.g. `459CD3`)
pub icao_number: String,
pub icao_number: Arc<str>,
/// The tail number of the aircraft (e.g. `OY-GFS`)
pub tail_number: String,
/// The ICAO number of the aicraft model (e.g. `F2TH`)
Expand Down Expand Up @@ -131,7 +131,7 @@ pub async fn load_aircrafts(
Some((
tail_number.clone(),
Aircraft {
icao_number: icao_number.to_ascii_lowercase(),
icao_number: icao_number.to_ascii_lowercase().into(),
tail_number,
model,
},
Expand Down
Loading

0 comments on commit e730ccb

Please sign in to comment.