Public
Edited
Nov 26
Insert cell
Insert cell
G2 = require("@antv/g2@4.0.2");
Insert cell
G2Plot = require('https://cdn.jsdelivr.net/npm/@antv/g2plot@2.4.23/dist/g2plot.min.js')
Insert cell
RUImageCredit = FileAttachment("RU_logo_nogr-300x130.png").image()
Insert cell
barChartSimple = {
const wrapper = html`
<div style="display: flex; flex-direction: column; align-items: flex-start; width: 100%; max-width: 800px; position: relative;">
<div style="width: 100%;">
<h3 style="text-align: left; padding-bottom: 8px; margin-bottom: 0; font-family: Helvetica; color: black; max-width: 100%;">
Οι Έλληνες εφοπλιστές που έχουν μεταφέρει ρωσικό πετρέλαιο από τον αγωγό CPC
</h3>
<p style="text-align: left; font-size: 16px; margin-top: 4px; font-family: Helvetica; color: black; max-width: 100%;">
Ο πίνακας παρουσιάζει το σύνολο των Ελλήνων εφοπλιστών, τάνκερ συμφερόντων των οποίων έχουν μεταφέρει ρωσικό πετρέλαιο από τον αγωγό CPC μετά την εισβολή της Ρωσίας στην Ουκρανία (1.3.2022 - 9.11.2024).
</p>
<p style="text-align: left; font-size: 12px; margin-top: 4px; font-family: Helvetica; color: black; max-width: 100%; font-weight: bold;">
Πατήστε τα κουμπιά του πίνακα, για να δείτε την κατάταξη των χωρών ανά αριθμό δρομολογίων και όγκο φορτίων.
</p>
</div>
<div style="display: flex; flex-direction: row; justify-content: flex-start; align-items: center; margin-bottom: 10px; width: 100%; padding-left: 0px; box-sizing: border-box;">
<button id="dromolologiaButton" style="margin-right: 10px; padding: 5px 10px; border: 1px solid #ccc; background-color: #3937b5; color: white; cursor: pointer; font-weight: bold; font-family: Helvetica; font-size: 14px;">Αριθμός δρομολογίων</button>
<button id="tonoiButton" style="padding: 5px 10px; border: 1px solid #ccc; background-color: white; color: black; cursor: pointer; font-weight: bold; font-family: Helvetica; font-size: 14px;">Ογκος φορτίων</button>
</div>

<div id="container" style="height: 350px; width: 100%;"></div>
<div style="display: flex; justify-content: space-between; width: 100%; padding: 10px; font-family: Helvetica; color: black;">
<div style="display: flex; flex-direction: column;">
<p style="margin: 0; font-size: 11px; line-height: 1.4em; text-align: left; color: black;">
<strong>Πηγή:</strong> Kpler, Equasis
</p>
<p style="margin: 0; font-size: 11px; line-height: 1.4em; text-align: left; color: black;">
<strong>Διαχείριση δεδομένων: </strong>Σωτήρης Σιδέρης / Reporters United
</p>
</div>
<img src=${await RUImageCredit.src} alt="RU Image Credit" style="height: 40px; width: auto;">
</div>
</div>
`;

const container = wrapper.querySelector('#container');

// Custom formatter for numbers using dot as thousands separator
const dotFormatter = (value) => {
return new Intl.NumberFormat('de-DE').format(value);
};

let dataDromolologia = [
{ "Country": "Διαμαντής Διαμαντίδης", "Value": 31 },
{ "Country": "Γιώργος Οικονόμου", "Value": 21 },
{ "Country": "Γιάννης Αλαφούζος", "Value": 5 },
{ "Country": "Γιώργος Προκοπίου", "Value": 5 },
{ "Country": "Νικόλας Μαρτίνος", "Value": 3 },
{ "Country": "Ανδρέας Μαρτίνος", "Value": 1 },
{ "Country": "Σπύρος Πολέμης", "Value": 1 },
{ "Country": "Νικόλας Τσάκος", "Value": 1 }
];
let dataTonoi = [
{ "Country": "Διαμαντής Διαμαντίδης", "Value": 4925229 },
{ "Country": "Γιώργος Οικονόμου", "Value": 3149158 },
{ "Country": "Γιάννης Αλαφούζος", "Value": 789211 },
{ "Country": "Γιώργος Προκοπίου", "Value": 764002 },
{ "Country": "Νικόλας Μαρτίνος", "Value": 430787 },
{ "Country": "Ανδρέας Μαρτίνος", "Value": 163417 },
{ "Country": "Σπύρος Πολέμης", "Value": 149992 },
{ "Country": "Νικόλας Τσάκος", "Value": 105365 },

];
const addRankings = (data) => {
const sortedData = [...data].sort((a, b) => b.Value - a.Value);
sortedData.forEach((item, index) => {
item.Rank = index + 1;
item.CountryWithRank = `${item.Rank}. ${item.Country}`;
});
return sortedData;
};

const renderBarChart = (data, valueField, xAxisTitle) => {
const rankedData = addRankings(data);
container.innerHTML = ''; // Clear the container

// Adjusting formatter based on valueField
const xAxisOptions = {
title: {
text: xAxisTitle,
style: {
fontFamily: 'Helvetica',
fontSize: 12,
fill: 'grey',
}
},
label: {
formatter: (value) => dotFormatter(value)
},
// Set tick interval to 2 for the tonnage in millions
tickInterval: valueField === 'Dromolologia' ? 10 : valueField === 'Tonoi' ? 1 : undefined,
min: valueField === 'Dromolologia' ? 0 : valueField === 'Tonoi' ? 0 : undefined,
max: valueField === 'Dromolologia' ? 40 : valueField === 'Tonoi' ? 5 : undefined,
nice: true,
};

const barChart = new G2Plot.Bar(container, {
data: rankedData.map(d => ({ ...d, Value: valueField === 'Tonoi' ? d.Value / 1000000 : d.Value })),
xField: 'Value',
yField: 'CountryWithRank',
seriesField: 'Country',
legend: false,
xAxis: xAxisOptions,
color: '#3937b5',
yAxis: {
label: {
style: {
fontWeight: 'bold',
color: 'black',
fontSize: 14
},
formatter: (text) => text
}
},
tooltip: {
showMarkers: false,
customContent: (title, items) => {
if (!items || items.length === 0) return ''; // Corrected this line
const datum = items[0].data;
const originalValue = (valueField === 'Tonoi') ? datum.Value * 1000000 : datum.Value;
const formattedValue = dotFormatter(originalValue);
const suffix = (valueField === 'Dromolologia' && originalValue === 1) ? 'δρομολόγιο' : (valueField === 'Dromolologia' ? 'δρομολόγια' : 'μετρικοί τόνοι');
return `<div style="font-family: Helvetica; color: black; max-width: 500px; line-height: 1.5; word-wrap: break-word;">
<strong>${datum.Country}:</strong> ${formattedValue} ${suffix}
</div>`;
},
domStyles: {
'g2-tooltip': {
backgroundColor: 'white',
border: '1px solid #ccc',
fontFamily: 'Helvetica',
color: 'black'
},
}
}
});

barChart.render();
};



const highlightButton = (buttonId) => {
document.getElementById('dromolologiaButton').style.backgroundColor = 'white';
document.getElementById('dromolologiaButton').style.color = 'black';
document.getElementById('tonoiButton').style.backgroundColor = 'white';
document.getElementById('tonoiButton').style.color = 'black';

const activeButton = document.getElementById(buttonId);
activeButton.style.backgroundColor = '#3937b5'; // Dark blue when active
activeButton.style.color = 'white';
};

// Initial render
requestAnimationFrame(() => {
document.getElementById('dromolologiaButton').addEventListener('click', () => {
renderBarChart(dataDromolologia, 'Dromolologia', 'Δρομολόγια');
highlightButton('dromolologiaButton');
});

document.getElementById('tonoiButton').addEventListener('click', () => {
renderBarChart(dataTonoi, 'Tonoi', 'Εκατομμύρια μετρικοί τόνοι (DWT)');
highlightButton('tonoiButton');
});

renderBarChart(dataDromolologia, 'Dromolologia', 'Δρομολόγια'); // Default view
highlightButton('dromolologiaButton');
});

return wrapper;
}

Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more