Les conséquences de la guerre au Moyen-Orient se font désormais sentir très clairement sur les prix des carburants dans les stations-service françaises.

Le blocage du détroit d’Ormuz perturbe le trafic mondial de pétrole, provoquant une flambée du prix du baril, et donc, par ricochet, l’augmentation des prix payés à la pompe par les consommateurs.

Voici une carte pour consulter les prix des différents carburants dans près de 10 000 stations-service françaises, en métropole et en Corse.

Protomaps © OpenStreetMap

Répartition des stations-services selon le prix

Prix moyen du gazole : .

Prix le moins élevé : .

Prix le plus élevé : .

${(isMobile ? `Utilisez deux doigts pour zoomer et un seul pour déplacer la carte.` : `Utilisez la molette de votre souris pour zoomer et le clic gauche pour déplacer la carte.`) +  » Cliquez sur un point pour découvrir le prix de l’essence de cette station. »}

Source : data.gouv.fr.

`;
}

let selectedCarburant = « Gazole »;

const carburantString = {
« Gazole »: « gazole »,
« SP95 »: « SP95 »,
« SP98 »: « SP98 »,
« GPLc »: « GPL-c »,
« E85 »: « E85 »,
« E10 »: « E10 »,
}

function pointToComma(str) {
return String(str).replace(/./g, « , »);
}
function formatDuration(date) {
const now = new Date();
const diff = now – date;
const diffInMinutes = Math.round(diff / 60000);

if (diffInMinutes < 1) {
return « moins d’une minute »;
} else if (diffInMinutes < 60) {
return `${diffInMinutes} minute${diffInMinutes > 1 ? « s » : «  »}`;
} else if (diffInMinutes < 1440) {
const hours = Math.round(diffInMinutes / 60);
return `${hours} heure${hours > 1 ? « s » : «  »}`;
} else if (diffInMinutes < 43200) {
const days = Math.round(diffInMinutes / 1440);
return `${days} jour${days > 1 ? « s » : «  »}`;
} else {
const months = Math.round(diffInMinutes / 43200);
return `${months} mois`;
}
}

async function initMap() {
const carburants = last_update;
const correspondance = await d3.csv(
« https://assets-decodeurs.lemonde.fr/sheets/do7ejUU8V-98xRlCzWZHrUGqf9vWcg_3736.csv »,
);
const quantiles = await d3.csv(
« https://assets-decodeurs.lemonde.fr/sheets/do7ejUU8V-98xRlCzWZHrUGqf9vWcg_3815.csv »,
);
const scaleIft = d3.scaleThreshold(
[0.25, 0.5, 1, 3, 5],
[
« #f7f7f7 »,
« #e7d4e8 »,
« #c2a5cf »,
« #9970ab »,
« #762a83 »,
« #40004b »,
],
);
const scaleSeuil = d3.scaleThreshold(
[1, 2, 3, 4],
[« #f2f0f7 », « #cbc9e2 », « #9e9ac8 », « #756bb1 », « #54278f »],
);
const boundsMetropole = [
[-5.708196140596648, 41.159442862997096],
[10.502321973141084, 52.10689424982629],
];
const boundsReunion = [
[55.07775392654938, -21.44419066387674],
[55.964781099227224, -20.774491046214454],
];
const boundsGuyane = [
[-54.985916441461164, 1.860861476138673],
[-51.12804741318951, 5.636713160986992],
];
const boundsAntilles = [
[-63, 13.64581],
[-59, 17.3],
];
const boundsMayotte = [
[44.9, -13.1],
[45.4, -12.5],
];
const boundsPolynesie = [
[-150.01993671781142, -18.031020108402718],
[-149.03472792054333, -17.281704029832003],
];

let layerStationsServices;

if (carburants) {
hideSpinner();

let tooltip = d3.select(« .carte_carburant__tooltip »);

function fillTooltip({ x, y, object }) {
tooltip.classed(« tooltip–hidden », false);

const stationsCarburants = {
« Prix du Gazole »: « Gazole »,
« Prix du SP95 »: « SP95 »,
« Prix du SP98 »: « SP98 »,
« Prix du GPL-c »: « GPLc »,
« Prix du E85 »: « E85 »,
« Prix du E10 »: « E10 »,
};

const content = `

${object.nom1 || « Station service  » + object.adress}

${object.nom1 ? `

${object.adress}

` : «  »}

    ${Object.entries(stationsCarburants)
    .filter(
    ([nomFormate, nomClef]) =>
    object[« prix_ » + nomClef] &&
    +object[« prix_ » + nomClef] > 0,
    )
    .map(
    ([nomFormate, nomClef]) =>
    `
  • ${nomFormate} : ${pointToComma(object[« prix_ » + nomClef])} €
    ${object[« maj_ » + nomClef]
    ? `
    (données mis à jour il y a ${formatDuration(new Date(object[« maj_ » + nomClef]))} ${new Date(object[« maj_ » + nomClef]) < new Date(Date.now() – 7 * 24 * 60 * 60 * 1000) ? « , susceptibles d’avoir fortement évolué » : «  »})
    `
    : «  »}
  • `,
    )
    .join(«  »)}

`;

// Fill tooltip
tooltip
.select(« .tooltipcontent »)
.attr(« aria-label », « Articles politiquess »)
.html(content);

tooltip.select(« .tooltipcross »).on(« click », onMouseOut);

// Display and translate tooltip

if (isMobile) {
tooltip
.classed(« tooltip–hidden », false)
.style(« bottom », 0)
.style(« left », 0);
} else {
tooltip
.classed(« tooltip–hidden », false)
.style(« bottom », `${y}px`)
.style(« left », `${x}px`);
}
}

// Hide tooltip

function onMouseOut() {
// document.querySelector(‘canvas’).style.cursor= »unset »;

tooltip.classed(« tooltip–hidden », true);
}

const mapSettings = {
center: [4.7, 47],
zoom: 4,
minZoom: 1,
maxZoom: 12,
bearing: 0,
pitch: 0,
};

const themeUrl = isDark
? « https://assets-decodeurs.lemonde.fr/decodeurs/assets/protomaps/contrast_daltoniens_darkmode_fr_v0.0.5.json »
: « https://assets-decodeurs.lemonde.fr/decodeurs/assets/protomaps/contrast_daltoniens_fr_v0.0.5.json »;

const theme = await d3.json(themeUrl);

const map = new maplibregl.Map({
container: « carte_carburant »,
style: {
version: 8,
glyphs: « https://assets-decodeurs.lemonde.fr/decodeurs/assets/protomaps_fonts/{fontstack}/{range}.pbf »,
sources: {
protomaps: {
type: « vector »,
maxzoom: 15,
tiles: [
« https://prd-protomap.8084.lemonde.io/20240111/{z}/{x}/{y}.mvt »,
],
},
},
layers: theme,
},
center: mapSettings.center,
zoom: mapSettings.zoom,
minZoom: mapSettings.minZoom,
maxZoom: mapSettings.maxZoom,
//pitch: mapSettings.pitch,
//bearing: mapSettings.bearing,
//maxBounds: mapSettings.bounds
});

map.on(« load », () => {
render();
});

const deckOverlay = new deck.MapboxOverlay({
onClick: ({ x, y, object }) => {
if (object == undefined) return onMouseOut();
const mapContainer =
document.getElementById(« carte_carburant »);
const adjustedY = mapContainer.offsetHeight – y;
return (
object && fillTooltip({ x, y: adjustedY, object })
);
},
onDragStart: () => onMouseOut(),
onResize: () => onMouseOut(),
pickingRadius: isMobile ? 20 : 8,
interleaved: false,
});

window.addEventListener(« click », (event) => {
if (
!event.target.closest(« .carte_carburant__tooltip ») &&
!event.target.closest(« canvas »)
) {
onMouseOut();
}
});

// Add deck layer
map.addControl(deckOverlay);

let scale = new maplibregl.ScaleControl({
maxWidth: 80,
unit: « metric »,
});

map.addControl(scale);

// Enable exploration

map.boxZoom.disable();
map.keyboard.disable();
map.doubleClickZoom.disable();
map.touchZoomRotate.disable();
map.dragRotate.disable();
map.touchPitch.disable();
map.scrollZoom.enable();
map.boxZoom.enable();
map.touchZoomRotate.enable();
map.dragPan.enable();

map.fitBounds(boundsMetropole);

// map.flyTo({
// center: mapSettings.center,
// zoom: mapSettings.zoom + 0.2,
// speed: 0.25,
// curve: 0.2,
// });

map.addControl(
new maplibregl.NavigationControl({ showCompass: false }),
« top-left »,
);

// Render layerspolys
function render() {
const filteredCarburants = carburants.filter(
(d) =>
+d[« prix_ » + selectedCarburant] &&
+d[« prix_ » + selectedCarburant] > 0,
);

if(selectedCarburant == « GPLc » ) {
console.log(filteredCarburants);}

const filteredCarburantRecent =
selectedCarburant == « GPLc »
? filteredCarburants
: filteredCarburants.filter(
(d) =>
new Date(d[« maj_ » + selectedCarburant]) >
new Date(
Date.now() – 7 * 24 * 60 * 60 * 1000,
),
);

const mean = d3.mean(
filteredCarburantRecent,
(d) => +d[« prix_ » + selectedCarburant],
);
const std = d3.deviation(
filteredCarburantRecent,
(d) => +d[« prix_ » + selectedCarburant],
);
const bornes = [
mean – 2 * std,
mean – std,
mean,
mean + std,
mean + 2 * std,
];

layerStationsServices = new deck.ScatterplotLayer({
id: « layerStationsServices »,
data: filteredCarburants,
getPosition: (d) => {
return [+d[« long »], +d[« lat »], 0];
},
getFillColor: (d) => {
if (
new Date(d[« maj_ » + selectedCarburant]) <
new Date(Date.now() – 7 * 24 * 60 * 60 * 1000)
) {
return [150, 150, 150];
}

const value = d3.interpolatePlasma(
1 –
(+d[« prix_ » + selectedCarburant] –
bornes[0]) /
(bornes[4] – bornes[0]),
);
const color = d3.color(value).rgb();
return [
Math.round(color.r),
Math.round(color.g),
Math.round(color.b),
];
},
getRadius: 500,
radiusMinPixels: 3,
radiusMaxPixels: 10,
pickable: true,
parameters: {
depthTest: false,
webgl2: false,
},
});

deckOverlay.setProps({ layers: [layerStationsServices] });

const min =
filteredCarburantRecent[
d3.minIndex(
filteredCarburantRecent,
(d) => +d[« prix_ » + selectedCarburant],
)
];
const max =
filteredCarburantRecent[
d3.maxIndex(
filteredCarburantRecent,
(d) => +d[« prix_ » + selectedCarburant],
)
];

container.querySelector(« .statistics »).innerHTML = `
Prix moyen du ${carburantString[selectedCarburant]} : ${pointToComma(mean.toFixed(2))} €.
Prix le plus bas : ${pointToComma(min[« prix_ » + selectedCarburant].toFixed(2))} € ${min[« departement »] ? `(${min[« departement »]})` : «  »}.
Prix le plus élevé : ${pointToComma(max[« prix_ » + selectedCarburant].toFixed(2))} € ${max[« departement »] ? `(${max[« departement »]})` : «  »}.
`;

container.querySelectorAll(« .flyto-price »).forEach((el) => {
el.onclick = () => {
const long = el.getAttribute(« data-long »);
const lat = el.getAttribute(« data-lat »);
map.flyTo({
center: [long, lat],
zoom: 10,
});
};
});

// D3 LEGENDE :
const legend = container.querySelector(
« .lmui-chart__legend »,
);

// D3 Bar Chart de répartition – bloc de 0.1 (1e à 1.1e, 1.1e à 1.2e, etc) :
const stationsPerPriceRange = Array.from(d3.group(
filteredCarburantRecent,
(d) => {
if (d[« prix_ » + selectedCarburant] < bornes[0]) {
return Math.floor(bornes[0] * 10) / 10;
} else if (d[« prix_ » + selectedCarburant] > bornes[4]) {
return Math.ceil(bornes[4] * 10) / 10;
} else {
return Math.floor(d[« prix_ » + selectedCarburant] * 10) / 10;
}
}

));

const priceRange = stationsPerPriceRange.map(([priceRange, stations]) => ({
priceRange,
nbStations: stations.length,
})).sort((a, b) => a.priceRange – b.priceRange);

const x = d3.scaleBand()
.domain(
priceRange.map((d) => d.priceRange)
)
.range([0, d3.select(« .chart-repartition-carburant svg »).node().getBoundingClientRect().width])
.padding(0.1);

const y = d3
.scaleSqrt()
.domain([0, d3.max(priceRange.map(priceRange => priceRange.nbStations))])
.range([0, 80]);

// d3.interpolatePlasma(
// 1 –
// (+d[« prix_ » + selectedCarburant] –
// bornes[0]) /
// (bornes[4] – bornes[0]),
// );

d3.select(« .chart-repartition-carburant svg g.rects »)
.selectAll(« rect »)
.data(priceRange)
.join(« rect »)
.attr(« x », (d, i) => x(x.domain()[i]))
.attr(« y », (d) => 105 – y(d.nbStations))
.attr(« width », x.bandwidth())
.attr(« height », (d) => y(d.nbStations))
.attr(« fill », d => d3.interpolatePlasma(1 – ((d.priceRange + 0.05 – bornes[0]) / (bornes[4] – bornes[0]))));

d3.select(« .chart-repartition-carburant svg g.texts »)
.selectAll(« text »)
.data(priceRange)
.join(« text »)
.attr(« x », (d, i) => x(x.domain()[i]) + x.bandwidth() / 2)
.attr(« y », (d) => 100 – y(d.nbStations) – 5)
.attr(« text-anchor », « middle »)
.text((d) => d.nbStations.toLocaleString(« fr-FR »))
.attr(« fill », « black »)
.attr(« font-size », « 12px »);

d3.select(« .chart-repartition-carburant svg g.legends »)
.selectAll(« text »)
.data(priceRange)
.join(« text »)
.attr(« x », (d, i) => x(x.domain()[i]) + x.bandwidth() / 2)
.attr(« y », 120)
.attr(« text-anchor », « middle »)
.attr(« font-weight », 500)
.text((d, i) => {
if (isMobile) {
if (i == 0) {
return `< ${d.priceRange.toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€`;
} else if (i == priceRange.length – 1) {
return `> ${d.priceRange.toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€`;
} else {
return `${(d.priceRange + 0.05).toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€`;
}
} else {
if (i == 0) {
return ` ${d.priceRange.toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€ et moins`;
} else if (i == priceRange.length – 1) {
return `${d.priceRange.toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€ et plus`;
} else {
return `${d.priceRange.toLocaleString(« fr-FR », {maximumFractionDigits: 1})} – ${(d.priceRange + 0.1).toLocaleString(« fr-FR », {maximumFractionDigits: 1})}€`;
}
}
})
.attr(« fill », « black »)
.attr(« font-size », « 12px »);

d3.select(« .repartition-title »).text(`Répartition des ${filteredCarburantRecent.length.toLocaleString(« fr-FR »)} stations-services selon le prix du ${carburantString[selectedCarburant]}`);

}

// Render first view
map.onload = () => {
render();
};
render();

// Watch flyto

document
.querySelectorAll(« .carte_carburant__flytobtn »)
.forEach((btn) => {
btn.onclick = () => {
const place = btn.getAttribute(« data-attr »);
switch (place) {
case « reset »:
map.fitBounds(boundsMetropole, {
animate: false,
});
break;
case « reunion »:
map.fitBounds(boundsReunion, {
center: [55.558823, -21.109403],
animate: false,
});

break;
case « guyane »:
map.fitBounds(boundsGuyane, {
center: [-53.14191, 4.113978],
animate: false,
});

break;
case « antilles »:
map.fitBounds(boundsAntilles, {
center: [-61.235294, 15.125274],
animate: false,
});

break;
case « mayotte »:
map.fitBounds(boundsMayotte, {
center: [45.150284, -12.800424],
animate: false,
});

break;
case « polynesie »:
map.fitBounds(boundsPolynesie, {
animate: false,
});
break;
default:
map.fitBounds(boundsMetropole, {
animate: false,
});
break;
}
};
});

/* AUTOCOMPLETE*/

let max_n_to_display = 6;
let min_char_to_search = 1;
let format_to_search = slugify;
let no_results_message = « Aucun résultat avec cette recherche »;

const reset_func = () => {};
const func_to_treat_result_donnees = (result) => {
map.flyTo({
center: [result[« Longitude »], result[« Latitude »]],
zoom: 10,
});

// if (isMobile) {
// fillTooltip({ x: 0, y: 0, object: result });
// document.querySelector(
// « .carte_carburant__container .tooltipcross »,
// ).onclick = () => {
// onMouseOut();
// };
// }
};

container
.querySelector(« #carburant-select »)
.addEventListener(« change », (e) => {
selectedCarburant = e.target.value;
render();
});

const lastSearch = new Date();

async function autocomplete_communes_decodeurs(
id_container,
func_to_treat_result,
func_to_reset,
max_n_to_display = 5,
no_results_message = « Aucun résultat avec cette recherche »,
) {
const get_search_value_in_data = (f) => {
return f.properties.label;
};
// OnSearch / OnResults
const onSearch = async (currentValue) => {
// Typical search
if (currentValue.replaceAll(/s/g, «  »).length < 3)
return;
const q = currentValue.replaceAll(/s/g, « + »);
const api_communes = `https://data.geopf.fr/geocodage/search/?q=${q}&type=municipality&limit=${max_n_to_display}`;

const data = await fetch(api_communes);
const rep = await data.json();
const data_communes = rep.features;

return data_communes;
};
await _generic_autocomplete(
id_container,
get_search_value_in_data,
onSearch,
func_to_treat_result,
func_to_reset,
3,
no_results_message,
);
}

autocomplete_communes_decodeurs(
// L’id du lmui-search
« prix-essence-search »,

//Une fonction qui s’execute quand on sélectionne un item
(result) => {
map.flyTo({
center: result.geometry.coordinates,
zoom: 10,
});
},

// Une fonction qui s’execute quand on désélectionne / reset
reset_func,

// Nombre de choix dans le autocomplete, par défaut 5
max_n_to_display,

// Nombre de lettres avant le déclenchement de l’autocomplete, par défaut 3
min_char_to_search,

// Une fonction qui normalise les input pour la recherche, par défaut slugify,
// c’est à dire que la casse et les accents sont ignorés
format_to_search,

// Texte à afficher si aucun resultat, par défaut « Aucun résultat avec cette recherche »
no_results_message,
);
}
}

// const INIT_DELAY = 2000; // 2 secondes
document.addEventListener(« DOMContentLoaded », () => {
fillHtml();
// d3.select(« .lmui-chart__legend »)
// .append(« svg »)
// .attr(« width », 300)
// .attr(« height », 50);

let chart = d3.select(« .chart-repartition-carburant »)
.append(« svg »)
.attr(« width », « 100% »)
.attr(« height », 125)

chart.append(« g »).attr(« class », « rects »);
chart.append(« g »).attr(« class », « texts »);
chart.append(« g »).attr(« class », « legends »);
initMap();
// setTimeout(() => {
// initMap();
// }, INIT_DELAY);
});
window.onresize = () => {
isMobile = window.innerWidth <= 600;
};
}

initViz();

addEventListener(« webglcontextlost », (event) => {
console.error(event);
alert(« bug »);
});

L’espace des contributions est réservé aux abonnés.

Abonnez-vous pour accéder à cet espace d’échange et contribuer à la discussion.

S’abonner
Share.
Exit mobile version