Unlisted
Edited
Aug 9, 2023
Insert cell
Changed in parent
-
# Klimawidget Update
+
# Carbon Emissions
Insert cell
Removed in parent
legend_emissions = swatches({
color: d3.scaleOrdinal(["frühindustrialisierte Länder", "andere"], ["rgb(153,175,194,.5)","rgb(79,128,255,.8)"])
})
Insert cell
Added in parent
min_emission = d3.min(carbon_emissions_total.map((d) => d.total))
Insert cell
Added in parent
anomaly_reduced = Plot.plot({
  width: chart_width,
  height: height,
  marginTop: 70,
  marginLeft: 0,
  marginRight: 0,
  marginBottom: 40,

  y: {
    axis: null
  },

  x: {
    axis: null
  },

  marks: [
    // Balken
    Plot.barY(global_monthly_filtered, {
      y: "Anomalie",
      x: "Jahr",
      strokeWidth: 0,
      fill: (d) =>
        (d.Jahr == 2025) | (d.Jahr == start) ? "#e84f1c" : grey_color,
      fillOpacity: (d) => (d.Jahr == start ? 0.2 : 1)
    }),

    // Monat
    Plot.text(anomaly_text, {
      x: (d) => d.year,
      y: "max_anomaly",
      text: (d) => d.month + " " + d.year,
      textAnchor: "end",
      fill: "#e84f1c",
      fontSize: 12,
      fontFamily: "InterRegular",
      dy: -60,
      dx: 0
    }),

    // große Zahl
    Plot.text(anomaly_text, {
      x: "year",
      y: "max_anomaly",
      text: (d) =>
        "+" +
        d.text.toLocaleString("de", {
          minimumFractionDigits: "2",
          maximumFractionDigits: "2"
        }) +
        "°C",
      textAnchor: "end",
      fill: "#e84f1c",
      fontSize: 22,
      fontWeight: 600,
      fontFamily: "DIN Next LT Pro",
      dy: -40,
      dx: 0
    }),

    // Definition
    Plot.text(anomaly_text, {
      x: "year",
      y: "max_anomaly",
      text: (d) => `globale Durchschnittstemperatur\nAbweichung von 1991-2000`,
      textAnchor: "end",
      fontFamily: "InterRegular",
      dy: -15,
      dx: 0
    }),

    // kleine Zahl
    Plot.text(anomaly_text_2, {
      x: "year",
      y: "min_anomaly",
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "1",
          maximumFractionDigits: "1"
        }) +
        "°C im " +
        d.month +
        " " +
        d.year,
      textAnchor: "start",
      fill: "#e84f1c",
      fillOpacity: 0.8,
      fontSize: 12,
      fontWeight: 600,
      fontFamily: "InterRegular",
      dy: 10,
      dx: 0
    }),

    // Quelle
    Plot.text(anomaly_text, {
      x: "year",
      y: "min_anomaly",
      text: (d) => `Quelle: Copernicus Climate Change Service`,
      textAnchor: "end",
      fill: "#99AFC2",
      fontFamily: "InterRegular",
      dy: 30,
      dx: 0
    })
  ]
})
Insert cell
Added in parent
anomaly_text = [
  {
    year: 2025,
    max_anomaly: 0.73,
    min_anomaly: -0.17,
    text: 0.73,
    month: "März"
  }
]
Insert cell
Added in parent
anomaly_text_2 = [
  {
    year: 2001,
    max_anomaly: 0.69,
    min_anomaly: -0.18,
    text: -0.06,
    month: "August"
  }
]
Insert cell
Added in parent
global_monthly_filtered_data = FileAttachment(
  "klimawidget_anomaly_march_2025.csv"
).csv()
Insert cell
Added in parent
global_monthly_filtered = global_monthly_filtered_data.map((d) => ({
  Jahr: +d.Jahr,
  Anomalie: +d.Anomalie
}))
Insert cell
Added in parent
months = [
  "Januar",
  "Februar",
  "März",
  "April",
  "Mai",
  "Juni",
  "Juli",
  "August",
  "September",
  "Oktober",
  "November",
  "Dezember"
]
Insert cell
Added in parent
forest_reduced = Plot.plot({
  width: chart_width,
  height: height,
  marginTop: 70,
  marginLeft: 10,
  marginRight: 10,
  marginBottom: 40,

  x: {
    axis: null
  },

  y: {
    axis: null
  },

  marks: [
    // Fläche
    Plot.areaY(forest_loss_data, {
      x: "year",
      y: "loss",
      curve: "monotone-x",
      fill: grey_color
    }),

    // Linie
    Plot.lineY(forest_loss_data, {
      x: "year",
      y: "loss",
      curve: "monotone-x",
      stroke: "#6bb024"
    }),

    // Punkte
    Plot.dot(forest_loss_data, {
      x: "year",
      y: "loss",
      stroke: "#6bb024",
      strokeOpacity: (d) => ((d.year == 2023) | (d.year == 2001) ? 1 : 0),
      fillOpacity: (d) => ((d.year == 2023) | (d.year == 2001) ? 1 : 0),
      fill: "#ffffff"
    }),

    // Jahr
    Plot.text(forest_text, {
      x: (d) => d.year,
      y: "max_forest",
      text: (d) => d.year.toString(),
      textAnchor: "end",
      fill: "#6bb024",
      fontSize: 12,
      fontFamily: "InterRegular",
      dy: -60,
      dx: 0
    }),

    // große Zahl
    Plot.text(forest_text, {
      x: "year",
      y: "max_forest",
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "0",
          maximumFractionDigits: "0"
        }) + " km²",
      textAnchor: "end",
      fill: "#6bb024",
      fontSize: 22,
      fontWeight: 600,
      fontFamily: "DIN Next LT Pro",
      dy: -40,
      dx: 0
    }),

    // Definition
    Plot.text(forest_text, {
      x: "year",
      y: "max_forest",
      text: (d) => `Waldverluste weltweit\n(ohne Waldgewinne)`,
      textAnchor: "end",
      fontFamily: "InterRegular",
      dy: -15,
      dx: 0
    }),

    // kleine Zahl
    Plot.text(forest_text_2, {
      x: "year",
      y: 0,
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "0",
          maximumFractionDigits: "0"
        }) +
        " km² im Jahr " +
        d.year,
      textAnchor: "start",
      fill: "#6bb024",
      fillOpacity: 0.8,
      fontSize: 12,
      fontWeight: 600,
      fontFamily: "InterRegular",
      dy: 10,
      dx: 0
    }),

    // Quelle
    Plot.text(forest_text, {
      x: "year",
      y: "min_forest",
      text: (d) => `Quelle: Global Forest Watch`,
      textAnchor: "end",
      fontFamily: "InterRegular",
      fill: "#99AFC2",
      dy: 30,
      dx: 0
    })
  ]
})
Insert cell
Added in parent
forest_loss_data = FileAttachment("wald_global_time_2023@2.csv")
  .csv()
  .then(function (data) {
    data.forEach(function (d) {
      d.loss = +d.loss;
      d.year = +d.year;
    });
    return data;
  })
Insert cell
Added in parent
forest_text = [
  {
    year: year_forest_last,
    max_forest: max_forest,
    min_forest: 0,
    text: recent_forest
  }
]
Insert cell
Added in parent
year_forest_last = d3.max(forest_loss_data.map((d) => d.year))
Insert cell
Added in parent
recent_forest = +forest_loss_data[0].loss
Insert cell
Added in parent
max_forest = d3.max(forest_loss_data.map((d) => d.loss))
Insert cell
Added in parent
min_forest = forest_loss_data[forest_loss_data.length - 1].loss
Insert cell
Added in parent
forest_text_2 = [
  {
    year: year_forest_first,
    max_forest: max_forest,
    min_forest: min_forest,
    text: first_forest
  }
]
Insert cell
Added in parent
year_forest_first = d3.min(forest_loss_data.map((d) => d.year))
Insert cell
Added in parent
first_forest = +forest_loss_data[forest_loss_data.length - 1].loss
Insert cell
Added in parent
klimawidget_title = md`<div class="mode" style="margin-bottom: 30px;"><h2 class="mode">Klimawandel in Grafiken</h2></div>`
Insert cell
Added in parent
klimawidget_zwei = html`
<div>
${klimawidget_title}
  <div class="box">


    <div class="box">

        <div class="box_one">
          ${emission_widget}
          ${credits_emission}
    </div>

    <div class="box_two">
          ${temp_widget} 
          ${credits_temp}
          
    </div>

</div>`
Insert cell
Added in parent
temp_widget = Plot.plot({
  width: chart_width,
  height: 250,
  marginLeft: 0,
  marginRight: 0,

  y: {
    axis: "left",
    domain: [-0.3, 1.5],
    ticks: []
  },

  x: {
    ticks: [1900, 1950, 2000],
    tickFormat: d3.format(""),
    tickSize: 4
  },

  marks: [
    // Balken
    Plot.barY(global_temp, {
      y: "degree",
      x: "jahr",
      stroke: "#ffffff",
      strokeWidth: 0.4,
      fill: (d) => (d.degree > 0 ? "#e84f1c" : "#4f80ff"),
      title: (d) => `${d.year}`,
      sort: { y: "y", reverse: true },
      opacity: (d) => (d.jahr == 2022 ? 1 : 0.75)
    }),

    // weiße Linien
    Plot.gridY({
      interval: 0.25,
      stroke: isDarkMode() ? "#293845" : "#ffffff",
      strokeOpacity: 1
    }),

    Plot.text([0], {
      x: 1850,
      y: 1.5,
      text: (d) =>
        `globale Durchschnittstemperatur 2022 im\nVerleich zur vorindustriellen Zeit in °C`,
      textAnchor: "start",
      lineAnchor: "top",
      dy: 0,
      dx: 0
    }),

    // Beschriftung rechtsbündig
    Plot.text(text_temp_end, {
      x: "x",
      y: "y",
      text: "text",
      textAnchor: "end",
      dy: 0,
      dx: 0
    }),

    // hervorstehende Zahl
    Plot.text(text_zahl, {
      x: "x",
      y: "y",
      text: "text",
      textAnchor: "start",
      fill: "#e84f1c",
      fontSize: 24,
      fontWeight: 600,
      dy: 45,
      dx: 0
    })
  ]
})
Insert cell
Added in parent
credits_temp = md`<span style="font-family: InterRegular; font-size: 12px; color: #99AFC2;">Quelle: Met Office<span>`
Insert cell
Added in parent
// https://climate.metoffice.cloud/temperature.html#datasets
global_temp = FileAttachment("global_temperature_berkeley@2.csv")
  .csv()
  .then(function (data) {
    data.forEach(function (d) {
      d.degree = +d.degree;
      d.jahr = +d.year;
    });
    return data;
  })
Insert cell
Added in parent
text_zahl = [{ x: 1850, y: 1.5, text: "+1,17°" }]
Insert cell
Added in parent
text_temp_end = [
  { x: 1937, y: 0.25, text: "+0,25°" },
  { x: 1978, y: 0.5, text: "+0,5°" },
  { x: 1995, y: 0.75, text: "+0,75°" },
  { x: 2005, y: 1, text: "+1,0°" }
]
Insert cell
Added in parent
carbon_emissions_axis = FileAttachment(
  "carbon_emissions_observable_total@2.csv"
)
  .csv()
  .then(function (data) {
    data.forEach(function (d) {
      d.emissions = +d.emissions / 1000;
      d.year = +d.year;
    });
    return data;
  })
Insert cell
Changed in both
-
emission_widget = Plot.plot({ width: chart_width, height: 250, marginLeft: 0, marginRight: 0,
+
Plot.plot({ width: 400, height: 300, marginLeft: 30,
x: { tickFormat: d3.format(""), label: null,
-
tickSize: 4, ticks: [1900, 1950, 2000]
+
ticks: 3
}, y: {
-
axis: null, ticks: []
+
tickFormat: (d) => d.toLocaleString("de"), grid: false, axis: "right", label: "Milliarden Tonnen"
}, color: { domain: group_order, range: color_scale },
-
marks: [
-
// Kurven einzeichnen
Plot.areaY(carbon_emissions, { x: "year", y: "emissions", fill: "group", z: "group",
-
stroke: "#f2f2f2", strokeWidth: 0.5, strokeOpacity: 0.5,
+
stroke: "black", // Add stroke
order: group_order,
-
tip: false
+
tip: true
}),
-
// Label für die Regionen Plot.text(text_group, {
+
// Use one text mark applied to a data array. Plot.text(text_data, {
x: "x", y: "y", text: "text", fontSize: 10, textAnchor: "end", lineAnchor: "bottom", fill: "white"
-
}), // Beschriftung Plot.text([0], { x: 1850, y: 40, text: (d) => `Globale Treibhausgasemissionen 2022\nin Milliarden Tonnen CO₂-Äquivalenten`, textAnchor: "start", lineAnchor: "top", dy: 0, dx: 0 }), // Achsenticks einzeichnen Plot.axisY({ x: (y) => carbon_emissions_axis.find((d) => d.emissions >= y)?.year, insetLeft: -3, domain: [0, 40], ticks: [10, 20, 30, 40] }), // Legende Plot.text([0], { x: 1850, y: 18, text: (d) => `veränderte Landnutzung`, textAnchor: "start", lineAnchor: "top", fill: "#99AFC2", dy: -15, dx: 0 }), Plot.text([0], { x: 1850, y: 18, text: (d) => `spät industrialisierte Länder`, textAnchor: "start", lineAnchor: "top", fill: "#e84f1c", dy: 0, dx: 0 }), Plot.text([0], { x: 1850, y: 18, text: (d) => `früh industrialisierte Länder`, textAnchor: "start", lineAnchor: "top", fill: "#4f80ff", dy: 15, dx: 0 }), // hervorstehende Zahl Plot.text(text_emission, { x: "x", y: "y", text: "text", textAnchor: "start", fill: "#4f80ff", fontSize: 24, fontWeight: 600, dy: 45, dx: 0
}) ] })
Insert cell
Added in fork
text_data = [
{ x: 2019, y: 8, text: "Rest" },
{ x: 2019, y: 7, text: "Indien" },
{ x: 2019, y: 5, text: "China" },
{ x: 2019, y: 0.6, text: "USA" },
{ x: 2019, y: 1.7, text: "EU" },
{ x: 2019, y: 2.8, text: `andere\nentwickelte` },
{ x: 2019, y: 3.9, text: "Russland" }
]
Insert cell
Added in parent
numbers = Array.from({ length: 800 }, (_, i) => i)
Insert cell
Added in parent
viewof timer = Player(numbers, {
  loop: true,
  alternate: false,
  show: { play: true }
})
Insert cell
Added in parent
klimawidget = html`

${klimawidget_title}
  <div class="box">

    <div class="box_one" style="display: ${first_box_one}">
          ${emission_reduced}
    </div>

    <div class="box_one" style="display: ${first_box_two}">
          ${forest_reduced}
    </div>

    <div class="box_two" style="display: ${second_box_two}">
        ${ice_reduced} 
    </div>

    <div class="box_two" style="display: ${second_box_one}">
          ${anomaly_reduced} 
    </div>

</div>

`
Insert cell
Added in parent
klima_button = html`
    <div style="height:40px;padding-top: 20px;"><a href="https://www.rnd.de/klima/" target="_blank" style="text-decoration: none;"><span class="klima">mehr zur Klimakrise</span></a></div>
`
Insert cell
Added in parent
first_box_one = {
  if (((timer >= 0) & (timer < 200)) | ((timer >= 400) & (timer < 600))) {
    return "inline";
  } else {
    return "none";
  }
}
Insert cell
Added in parent
first_box_two = {
  if (((timer >= 200) & (timer < 400)) | ((timer >= 600) & (timer <= 800))) {
    return "inline";
  } else {
    return "none";
  }
}
Insert cell
Added in parent
second_box_one = {
  if (
    ((timer >= 700) & (timer < 800)) |
    ((timer >= 0) & (timer < 100)) |
    ((timer >= 300) & (timer < 500))
  ) {
    return "inline";
  } else {
    return "none";
  }
}
Insert cell
Added in parent
second_box_two = {
  if (((timer >= 100) & (timer < 300)) | ((timer >= 500) & (timer < 700))) {
    return "inline";
  } else {
    return "none";
  }
}
Insert cell
Added in parent
start = 2001
Insert cell
Added in parent
height = 180
Insert cell
Added in parent
emission_opacity = isDarkMode() ? 0.8 : 0.2
Insert cell
Added in parent
emission_color = isDarkMode() ? "#eff3f5" : "#293845"
Insert cell
Added in parent
grey_color = isDarkMode() ? "#435b70" : "#eff3f5"
Insert cell
Added in parent
ice_reduced = Plot.plot({
  width: chart_width,
  height: height,
  marginTop: 70,
  marginLeft: 0,
  marginRight: 0,
  marginBottom: 40,

  x: {
    axis: null
  },

  y: {
    axis: null
  },

  marks: [
    // Balken
    Plot.barY(data_ice_filtered, {
      x: "year",
      y: "extent",
      fill: (d) =>
        (d.year == 2025) | (d.year == start) ? "#4f80ff" : grey_color,
      fillOpacity: (d) => (d.year == start ? 0.2 : 1),
      strokeWidth: 0
    }),

    // Tag
    Plot.text(ice_text, {
      x: (d) => d.day.getFullYear(),
      y: "max_ice",
      text: (d) =>
        d.day.toLocaleString("de", {
          day: "numeric",
          month: "numeric",
          year: "numeric"
        }),
      textAnchor: "end",
      fill: "#4f80ff",
      fontSize: 12,
      fontFamily: "InterRegular",
      dy: -60,
      dx: 0
    }),

    // große Zahl
    Plot.text(ice_text, {
      x: (d) => d.day.getFullYear(),
      y: "max_ice",
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "1",
          maximumFractionDigits: "1"
        }) + " Mio. km²",
      textAnchor: "end",
      fill: "#4f80ff",
      fontSize: 22,
      fontWeight: 600,
      fontFamily: "DIN Next LT Pro",
      dy: -40,
      dx: 0
    }),

    // Definition
    Plot.text(ice_text, {
      x: (d) => d.day.getFullYear(),
      y: "max_ice",
      text: (d) => `Ausdehnung des Meereseises\nder Antarktis`,
      textAnchor: "end",
      fontFamily: "InterRegular",
      dy: -15,
      dx: 0
    }),

    // kleine Zahl
    Plot.text(ice_text_2, {
      x: (d) => d.day.getFullYear(),
      y: "min_ice",
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "1",
          maximumFractionDigits: "1"
        }) +
        " Mio. km² am " +
        d.day.toLocaleString("de", {
          day: "numeric",
          month: "numeric",
          year: "numeric"
        }),
      textAnchor: "start",
      fill: "#4f80ff",
      fillOpacity: 0.8,
      fontSize: 12,
      fontWeight: 600,
      fontFamily: "InterRegular",
      dy: 10,
      dx: 0
    }),

    // Quelle
    Plot.text(ice_text, {
      x: (d) => d.day.getFullYear(),
      y: "min_ice",
      text: (d) => `Quelle: climatereanalyzer.org`,
      textAnchor: "end",
      fontFamily: "InterRegular",
      fill: "#99AFC2",
      dy: 30,
      dx: 0
    })
  ]
})
Insert cell
Added in parent
ice_text = [
  {
    day: day_ice_last,
    max_ice: max_ice,
    min_ice: 0,
    text: recent_ice
  },
]
Insert cell
Added in parent
day_ice_last = d3.max(data_ice_filtered.map((d) => d.date))
Insert cell
Added in parent
max_ice = d3.max(data_ice_filtered.map((d) => d.extent))
Insert cell
Added in parent
recent_ice = +data_ice_filtered[0].extent
Insert cell
Added in parent
ice_text_2 = [
  {
    day: day_ice_first,
    max_ice: max_ice,
    min_ice: 0,
    text: first_ice
  }
]
Insert cell
Added in parent
day_ice_first = d3.min(data_ice_filtered.map((d) => d.date))
Insert cell
Added in parent
first_ice = +data_ice_filtered[data_ice_filtered.length - 1].extent
Insert cell
Added in parent
//  https://nsidc.org/data/seaice_index/data-and-image-archive
url = "https://docs.google.com/spreadsheets/d/1Jss8jkdNOIQ4JptDUoV4z3pUVlLaENIa9CD2Yiqv8wg/edit?gid=1253017609#gid=1253017609"
Insert cell
Added in parent
rawdata_ice = d3.csv(getCsvUrl(url), d3.autoType)
Insert cell
Added in parent
data_ice = rawdata_ice
  .map((d) => ({
    year: +d.Year,
    month: +d.Month,
    day: +d.Day,
    extent: +d.Extent,
    date: roundUTCDay(new Date(d.Year + "-" + d.Month + "-" + d.Day)),
    date_repeat: roundUTCDay(new Date("2023" + "-" + d.Month + "-" + d.Day)),
    day_of_year:
      Math.floor(
        (roundUTCDay(new Date(d.Year + "-" + d.Month + "-" + d.Day)) -
          new Date(
            roundUTCDay(
              new Date(d.Year + "-" + d.Month + "-" + d.Day)
            ).getFullYear(),
            0,
            0
          )) /
          86400000
      ) - 1
  }))
  .filter((d) => (d.year != "YYYY") & (d.year >= start) & (d.day <= 364))
Insert cell
Added in parent
max_month_ice = d3.max(data_ice.map((d) => d.date)).getMonth() + 1
Insert cell
Added in parent
max_day_ice = d3.max(data_ice.map((d) => d.date)).getUTCDate()
Insert cell
Added in parent
SELECT year, date, extent FROM data_ice
WHERE month == ${max_month_ice}
AND day == ${max_day_ice}
ORDER BY year desc
Insert cell
Added in parent
emission_reduced = Plot.plot({
  width: chart_width,
  height: height,
  marginTop: 70,
  marginLeft: 10,
  marginRight: 10,
  marginBottom: 40,

  x: {
    axis: null
  },

  y: {
    axis: null
  },

  marks: [
    // Fläche
    Plot.areaY(carbon_emissions_total_concat, {
      x: "year",
      y: "total",
      curve: "monotone-x",
      fill: grey_color
    }),

    // Linie
    Plot.lineY(carbon_emissions_total_concat, {
      x: "year",
      y: "total",
      curve: "monotone-x",
      stroke: emission_color
    }),

    // Punkte
    Plot.dot(carbon_emissions_total_concat, {
      x: "year",
      y: "total",
      stroke: emission_color,
      strokeOpacity: (d) => ((d.year == 2023) | (d.year == start) ? 1 : 0),
      fillOpacity: (d) => ((d.year == 2023) | (d.year == start) ? 1 : 0),
      fill: "#ffffff"
    }),

    // Jahr
    Plot.text(emissions_text, {
      x: "year",
      y: "max_emission",
      text: (d) => d.year.toString(),
      textAnchor: "end",
      fill: emission_color,
      fontSize: 12,
      fontFamily: "InterRegular",
      dy: -60,
      dx: 0
    }),

    // große Zahl
    Plot.text(emissions_text, {
      x: "year",
      y: "max_emission",
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "1",
          maximumFractionDigits: "1"
        }) + " Mrd. t",
      textAnchor: "end",
      fill: emission_color,
      fontSize: 22,
      fontWeight: 600,
      fontFamily: "DIN Next LT Pro",
      dy: -40,
      dx: 0
    }),

    // Definition
    Plot.text(emissions_text, {
      x: "year",
      y: "max_emission",
      fontFamily: "InterRegular",
      text: (d) => `globale Treibhausgasemissionen\nin CO₂-Äquivalenten`,
      textAnchor: "end",
      dy: -15,
      dx: 0
    }),

    // kleine Zahl
    Plot.text(emissions_text_2, {
      x: "year",
      y: 0,
      text: (d) =>
        d.text.toLocaleString("de", {
          minimumFractionDigits: "1",
          maximumFractionDigits: "1"
        }) +
        " Mrd. t im Jahr " +
        d.year,
      textAnchor: "start",
      opacity: 0.8,
      fill: emission_color,
      fontSize: 12,
      fontWeight: 600,
      fontFamily: "InterRegular",
      dy: 10,
      dx: 0
    }),

    // Quelle
    Plot.text(emissions_text, {
      x: "year",
      y: "min_emission",
      text: (d) => `Quelle: Global Carbon Project`,
      textAnchor: "end",
      fill: "#99AFC2",
      fontFamily: "InterRegular",
      dy: 30,
      dx: 0
    })
  ]
})
Insert cell
Added in parent
emissions_text = [
  {
    year: d3.max(carbon_emissions_total_concat.map((d) => d.year)),
    max_emission: max_emission,
    min_emission: 0,
    text: recent_emission
  }
]
Insert cell
Added in parent
max_emission = d3.max(carbon_emissions_total.map((d) => d.total))
Insert cell
Added in parent
recent_emission = +carbon_emissions_total_concat[0].total
Insert cell
Added in parent
emissions_text_2 = [
  {
    year: d3.min(carbon_emissions_total_concat.map((d) => d.year)),
    max_emission: max_emission,
    min_emission:
      carbon_emissions_total_concat[carbon_emissions_total_concat.length - 1]
        .total,
    text: first_emission
  }
]
Insert cell
Added in parent
first_emission = +carbon_emissions_total_concat[
  carbon_emissions_total_concat.length - 1
].total
Insert cell
Added in parent
carbon_emissions_total_concat = carbon_2023.concat(
  carbon_2022.concat(carbon_emissions_total)
)
Insert cell
Added in parent
carbon_2022 = [{ year: 2022, total: 40.6 }]
Insert cell
Added in parent
carbon_2023 = [{ year: 2023, total: 40.9 }]
Insert cell
Added in parent
SELECT year, sum(emissions) as total FROM carbon_emissions
  WHERE year >= ${start}
  GROUP BY 1
  ORDER BY year desc
Insert cell
Changed in parent
-
// https://globalcarbonbudgetdata.org/latest-data.html carbon_emissions = FileAttachment("carbon_emissions_observable@4.csv")
+
carbon_emissions = FileAttachment("carbon_emissions_observable@1.csv")
.csv() .then(function (data) { data.forEach(function (d) { d.emissions = +d.emissions / 1000; d.year = +d.year; }); return data; })
Insert cell
Removed in parent
carbon_emissions
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Added in parent
credits_emission = md`<span style="font-family: InterRegular; font-size: 12px; color: #99AFC2;">Quelle: Global Carbon Project<span>`
Insert cell
Added in parent
text_group = [
  { x: 2020, y: 8.2 * 3.664, text: "Rest" },
  { x: 2020, y: 6.6 * 3.664, text: "Indien" },
  { x: 2020, y: 5 * 3.664, text: "China" },
  { x: 2020, y: 2.65 * 3.664, text: "Rest" },
  { x: 2020, y: 1.3 * 3.664, text: "USA" },
  { x: 2020, y: 0.2 * 3.664, text: "EU" }
]
Insert cell
Added in parent
text_emission = [{ x: 1850, y: 40, text: "41,1 Mrd. t" }]
Insert cell
Changed in parent
group_order = [
+
"USA",
"Europäische Union",
-
"USA", "andere entwickelte",
+
"andere entwickelte Länder", "Russland",
"China", "Indien",
-
"Russland", "Rest", "Landnutzung"
+
"Rest der Welt", "internationaler Flugverkehr"
]
Insert cell
Changed in parent
color_scale = [
+
"#4f80ff",
"#80A3FF",
-
"#B3C8FF", "#B3C8FF", "#ff6531", "#ff834c", "#ff834c", "#ff834c", "#eff3f5"
+
"#80A3FF", "#ff7842", "#e84f1c", "#ff7842", "#ff7842", "#ff7842", "#ff7842", "#ff7842"
]
Insert cell
Added in parent
chart_width = {
  if (window.innerWidth < 500) {
    return window.innerWidth;
  } else {
    return window.innerWidth / 2 - 22;
  }
}
Insert cell
Added in parent
isDarkMode = () =>
  window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
Insert cell
Added in parent
function roundUTCDay(d) {
  return new Date(
    Date.UTC(
      d.getUTCFullYear(),
      d.getUTCMonth(),
      d.getUTCDate() + (d.getUTCHours() < 12 ? 0 : 1)
    )
  );
}
Insert cell
Added in parent
getCsvUrl = (url) => {
  url = new URL(url);
  const id = url.pathname.split("/")[3];
  const gid = new URLSearchParams(url.hash.slice(1)).get("gid") || 0;
  return `https://docs.google.com/spreadsheets/d/${id}/export?format=csv&gid=${gid}`;
}
Insert cell
Added in parent
sb = `
margin-right: 0.3em;
width: 2em;
border-radius:0;
background-color:${bgc};
border: 0px solid #bbb;
outline: none
`
Insert cell
Added in parent
bgc = "#ffffff"
Insert cell
Added in parent
bgc2 = "#e84f1c"
Insert cell
Added in parent
function fas(s) {
  return `<i class="fas fa-${String.raw(...arguments)} fa-xs"></i>`;
}
Insert cell
Added in parent
faStyle({ solid: true })
Insert cell
Added in parent
import { disposal } from "@mbostock/disposal"
Insert cell
Added in parent
import { style as faStyle } from "@airbornemint/fontawesome"
Insert cell
Added in parent
function Player(
  values,
  {
    format = (value) => value,
    index = false,
    initial = 0,
    delay = 1000 / 20,
    autoplay = true,
    loop = false,
    alternate = false,
    width = 300,
    time = false,
    speedFactor = 1.5,
    debug = false,
    show = null
  } = {}
) {
  const delay0 = delay;
  const alternate0 = alternate;
  const loop0 = loop;

  values = Array.from(values);
  let idx = initial;
  let isPlay = false;
  let isAlternate = null;
  let isLoop = null;

  let isForward = true;
  let timer = null;
  let t0 = null;
  let t1 = null;

  const sf = `
    font: 12px var(--sans-serif);
    font-variant-numeric: tabular-nums;
    display: flex;
    height: 33px;
    align-items: center;
  `;

  const icons = {
    play: html`${fas`play`}`,
    pause: html`${fas`pause`}`,
    stop: html`${fas`stop`}`,
    speedUp: html`${fas`chevron-up`}`,
    speedDown: html`${fas`chevron-down`}`,
    toRight: html`${fas`arrow-right`}`,
    toLeft: html`${fas`arrow-left`}`,
    alternate: html`${fas`exchange-alt`}`,
    loop: html`${fas`redo`}`,
    stepRight: html`${fas`step-forward`}`,
    stepLeft: html`${fas`step-backward`}`
  };

  const btnDesc = [
    ["p", "play"],
    ["s", "stop"],
    ["l", "stepLeft"],
    ["r", "stepRight"],
    ["u", "speedUp"],
    ["d", "speedDown"],
    ["w", "toRight"],
    ["a", "alternate"],
    ["b", "loop"]
  ];

  const func = !show
    ? (acc, v) => ((acc[v] = true), acc)
    : Object.values(show).every((e) => e)
    ? (acc, v) => ((acc[v] = show[v] === true ? true : false), acc)
    : Object.values(show).every((e) => !e)
    ? (acc, v) => ((acc[v] = show[v] === false ? false : true), acc)
    : (acc, v) => ((acc[v] = show[v] || true), acc);

  const showBtn = btnDesc.map((e) => e[1]).reduce(func, {});
  showBtn.play = true;

  const btns = btnDesc.map(([x, kind]) => {
    if (!showBtn[kind]) return "";
    const icon = icons[kind];
    return html`<button name=${x} type=button style="${sb}">${icon}</button>`;
  });

  const subgroup = html`
    <div style="${sf}">
      ${btns}
      <div style="display: flex; align-items: center;">
        <input name=i type=range min=0 max=${
          values.length - 1
        } value=${idx} step=1 style="width: ${width}px;">
        <output name=o style="margin-left: 0.4em;">${format(
          values[idx]
        )}""</output>
      </div>
    </div>
  `;
  const form = html`
    <form style="display:flex; justify-content:space-between;">
      ${subgroup}
      <output name=t style="${sf}"></output>
    </form>
  `;
  function log(txt) {
    if (debug) console.log(txt);
  }
  function updateIconP() {
    const before = form.p.firstChild;
    const after = isPlay ? icons.pause : icons.play;
    form.p.replaceChild(after, before);
  }
  function updateIconW() {
    if (!showBtn.toRight) return;
    const before = form.w.firstChild;
    const after = isForward ? icons.toRight : icons.toLeft;
    form.w.replaceChild(after, before);
  }
  function updateIconStyleA() {
    if (!showBtn.alternate) return;
    form.a.style.background = isAlternate ? bgc2 : bgc;
  }
  function updateIconStyleB() {
    if (!showBtn.loop) return;
    form.b.style.background = isLoop ? bgc2 : bgc;
  }
  function updateForm() {
    form.i.valueAsNumber = idx;
    form.value = values[idx];
    form.o.value = format(form.value, form.i.valueAsNumber, values);
    dispatchEvent();
  }
  function dispatchEvent() {
    form.value = index ? idx : values[idx];
    form.dispatchEvent(new CustomEvent("input", { bubbles: true }));
  }
  function resetTime() {
    t0 = new Date();
  }
  function clearTime() {
    form.t.value = "";
  }
  function updateTime() {
    const t1 = new Date();
    const dt = (t1.getTime() - t0.getTime()) / 1000;
    if (time) form.t.value = `${dt.toFixed(2)} s`;
  }

  function step(isManualForward = null) {
    const _isForward = isManualForward === null ? isForward : isManualForward;
    const _canUpdateIsForward = isManualForward === null;

    if (isManualForward !== null) {
      log("_isForward", _isForward);
      log("_canUpdateIsForward", _canUpdateIsForward);
    }

    const first = 0;
    const last = values.length - 1;
    let move = true;
    if (_isForward) {
      if (idx < last) {
        idx++;
      } else if (isLoop) {
        if (isAlternate) {
          idx = last - 1;
          if (_canUpdateIsForward) isForward = false;
          updateIconW();
        } else {
          idx = first;
        }
      } else {
        move = false;
      }
    } else if (idx > first) {
      idx--;
    } else if (isLoop) {
      if (isAlternate) {
        idx = first + 1;
        if (_canUpdateIsForward) isForward = true;
        updateIconW();
      } else {
        idx = last;
      }
    } else {
      move = false;
    }
    if (!move) {
      log("no move");
      clearInterval(timer);
      isPlay = false;
      updateIconP();
    }
    updateForm();
    updateTime();
    log(idx);
  }

  function setTimer() {
    step();
    if (isPlay) timer = setTimeout(setTimer, delay);
  }

  function clickPlayPause() {
    if (isPlay) {
      log("clicked pause");
      isPlay = false;
      clearTimeout(timer);
    } else {
      log("clicked play");
      isPlay = true;
      resetTime();
      if (isForward && idx === values.length - 1) idx = 0;
      if (!isForward && idx === 0) idx = values.length - 1;
      updateIconP();
      setTimer();
    }
    updateIconP();
  }
  function clickStop() {
    log("clicked stop");
    clearInterval(timer);
    isPlay = false;
    idx = initial;
    delay = delay0;
    alternate = alternate0;
    loop = loop0;
    updateIconStyleA();
    updateIconStyleB();
    updateIconP();
    updateForm();
    clearTime();
  }
  function clickStepLeft() {
    log("clicked step left");
    if (!isPlay) {
      step(false);
    }
  }
  function clickStepRight() {
    log("clicked step right");
    if (!isPlay) {
      step(true);
    }
  }
  function clickSpeedUp() {
    log("clicked speed up");
    delay /= speedFactor;
    log(delay);
  }
  function clickSpeedDown() {
    log("clicked speed down");
    delay *= speedFactor;
    log(delay);
  }
  function clickWay() {
    log("clicked toggle way");
    isForward = !isForward;
    updateIconW();
  }
  function clickAlternate() {
    log("clicked toggle alternate");
    isAlternate = !isAlternate;
    updateIconStyleA();
  }
  function clickLoop() {
    log("clicked toggle loop");
    isLoop = !isLoop;
    updateIconStyleB();
  }

  if (showBtn.play) form.p.onclick = clickPlayPause;
  if (showBtn.stop) form.s.onclick = clickStop;
  if (showBtn.stepLeft) form.l.onclick = clickStepLeft;
  if (showBtn.stepRight) form.r.onclick = clickStepRight;
  if (showBtn.speedUp) form.u.onclick = clickSpeedUp;
  if (showBtn.speedDown) form.d.onclick = clickSpeedDown;
  if (showBtn.toRight) form.w.onclick = clickWay;
  if (showBtn.alternate) form.a.onclick = clickAlternate;
  if (showBtn.loop) form.b.onclick = clickLoop;

  form.i.oninput = (event) => {
    // console.log('form.i event', event, form.i.valueAsNumber, form.i.value);
    idx = form.i.valueAsNumber;
    updateForm();
  };

  if (alternate) clickAlternate();
  if (loop) clickLoop();
  if (autoplay) clickPlayPause();
  dispatchEvent();
  if (showBtn.stop) disposal(form).then(clickStop);

  const update = (i) => {
    idx = i;
    updateForm();
  };

  const remoteClickPlayPause = () => {
    clickPlayPause();
  };

  return Object.assign(form, { update, remoteClickPlayPause });
}
Insert cell
Changed in parent
style = html` <div></div> <style>
-
figure, .plot-d6a7b5 { margin-left: 0px; margin-right: 0px; padding: 0px; } .klima { background-color: #4f80ff; margin-bottom: 10px; margin-top: 30px; color: #ffffff; font-weight: 500; height: 40px; border: 1px solid rgb(79, 128, 255); padding: 10px 15px; border-radius: 4px; font-size: 14px; line-height: 18px; cursor: pointer; font-family: InterRegular; }
.box_one {
-
width: calc(50% - 10px); padding-top: 0px; padding-bottom: 0px;
+
width: calc(33% - 10px); padding-top: 10px; padding-bottom: 10px;
padding-left: 0px; padding-right: 10px; } .box_two {
-
width: calc(50% - 12px); padding-top: 0px; padding-bottom: 0px;
+
width: calc(33% - 22px); padding-top: 10px; padding-bottom: 10px;
padding-left: 10px;
+
padding-right: 10px; border-left: 2px dotted #c5d2dd; } .box_three { width: calc(33% - 12px); padding-left: 10px;
padding-right: 0px;
+
padding-top: 10px; padding-bottom: 10px;
border-left: 2px dotted #c5d2dd; }
-
@media (max-width: 500px) {
+
@media (max-width: 900px) {
-
.box_one {
+
.box_one, .box_two, .box_three {
width: 100%;
-
padding-top: 0px; padding-bottom: 20px;
+
padding-top: 10px; padding-bottom: 10px;
padding-left: 0px; padding-right: 0px; border-top: 0px dotted #c5d2dd; border-bottom: 2px dotted #c5d2dd; border-left: 0px dotted #c5d2dd; border-right: 0px dotted #c5d2dd; }
-
.box_two { width: 100%; padding-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; border-top: 0px dotted #c5d2dd; border-bottom: 0px dotted #c5d2dd; border-left: 0px dotted #c5d2dd; border-right: 0px dotted #c5d2dd; }
} .legend { bottom: 3px; position: relative; margin-right: 5px; width: 20px; height: 1px; background-color: #0f151a; display: inline-block;" } .link { color: #4f80ff; } .box { display: flex; flex-wrap: wrap; } .tooltip { fill: #fff; } .tooltip_text { color: #000; } .mode { color: #0f151a; background-color: #ffffff; } @font-face { font-family: Source Serif Pro; src: url(https://static.rndtech.de/share/rnd/jchrist/SourceSerifPro-Regular.ttf); } @font-face { font-display: swap; font-family: 'DIN Next LT Pro'; font-style: normal; font-weight: 700; src: url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterRegular'; font-style: normal; font-weight: 400; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterMedium'; font-style: normal; font-weight: 500; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterBold'; font-style: normal; font-weight: 700; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.ttf) format("truetype") } h2 { color: rgb(15, 21, 26); font-family: "DIN Next LT Pro", sans-serif; font-weight: 700; font-size: 22px; margin: 0px 0px 0px 0px; } .title { color: rgb(15, 21, 26); font-family: "DIN Next LT Pro", sans-serif; font-weight: 700; font-size: 22px; margin: 0px 0px 0px 0px; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: rgb(41, 56, 69); } .credits { font-size: 12px; color: #99AFC2; font-family: InterRegular, sans-serif; font-weight: 400; position: relative; } body { display: block; margin-left: 0px; margin-right: 0px; margin-bottom: 0px; margin-top: 0px; } svg { background-color: white !important; color: #0f151a !important; } .tooltip { fill: #fff; } .tooltip_text { color: #000; } g:not([font-size], [font-weight]) text { font-size: 12px; line-height: 125%; font-weight: 400; font-family: InterRegular; color: #0f151a; background-color: #ffffff; } @media (prefers-color-scheme: dark) { .legend { bottom: 3px; position: relative; margin-right: 5px; width: 20px; height: 1px; background-color: #ffffff; display: inline-block;" } .tooltip { fill: #293845; opacity: .85; } .tooltip_text { color: #fff; } .mode { color: #ffffff; background-color: #293845; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: #ffffff; } g:not([font-size], [font-weight]) text { font-size: 12px; font-weight: 400; font-family: InterRegular; color: #ffffff; background-color: #293845; } svg { background-color: #293845 !important; color: white !important; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: #ffffff; } g:not([font-size], [font-weight]) text { font-size: 12px; line-height: 125%; font-weight: 400; font-family: InterRegular; color: #ffffff; background-color: #293845; } } </style>`
Insert cell
Removed in parent
function entity(character) {
return `&#${character.charCodeAt(0).toString()};`;
}
Insert cell
Removed in parent
function swatches({
color,
columns = null,
format = (x) => x,
swatchSize = 15,
swatchWidth = swatchSize,
swatchHeight = swatchSize,
marginLeft = 0
}) {
const id = DOM.uid().id;

if (columns !== null)
return html`<div style="display: flex; align-items: center; margin-left: ${+marginLeft}px; font: 10px sans-serif;">
<style>

.${id}-item {
break-inside: avoid;
display: flex;
align-items: center;
padding-bottom: 1px;
}

.${id}-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(100% - ${+swatchWidth}px - 0.5em);
}

.${id}-swatch {
width: ${+swatchWidth}px;
height: ${+swatchHeight}px;
margin: 0 0.5em 0 0;
}

</style>
<div style="width: 100%; columns: ${columns};">${color
.domain()
.map((value) => {
const label = format(value);
return html`<div class="${id}-item">
<div class="${id}-swatch" style="background:${color(value)};"></div>
<div class="${id}-label" title="${label.replace(
/["&]/g,
entity
)}">${document.createTextNode(label)}</div>
</div>`;
})}
</div>
</div>`;

return html`<div style="display: flex; align-items: center; min-height: 33px; margin-left: ${+marginLeft}px; font: 10px sans-serif;">
<style>

.${id} {
display: inline-flex;
align-items: center;
margin-right: 1em;
font-size: 12px;
line-height: 125%;
font-weight: 400;
font-family: InterRegular;
width: 100%;
}

.${id}::before {
content: "";
width: ${+swatchWidth}px;
height: ${+swatchHeight}px;
margin-right: 0.5em;
background: var(--color);
}

</style>
<div>${color
.domain()
.map(
(value) =>
html`<span class="${id}" style="--color: ${color(
value
)}">${document.createTextNode(format(value))}</span>`
)}</div>`;
}
Insert cell