{
source: "Captor 1",
measures: [
{
instant: 1495587600000,
value: 7390
},
{
instant: 1495591200000,
value: -5670
},
//...
}
Il existe plusieurs librairies pour faire des graphiques sur un site web. En ce moment j’utilise highcharts. Cette librairie a l’avantage de proposer un site avec beaucoup d’exemples et une bonne description de l’API.
Mais les options de paramétrages sont très nombreuses et il n’est pas toujours simple de faire un graphique qui colle à notre use case. Je vais prendre un exemple que j’ai rencontré récemment. Je devais créer un graphique composé de zones empilées (staked area) représentant des valeurs négatives et positives. Forcément si j’écris cet article, c’est que tout n’a pas été tout rose.
Prenons un exemple (les sources présentées ici sont disponibles sous Github) dans lequel nous essayons d’afficher des mesures temporelles
{
source: "Captor 1",
measures: [
{
instant: 1495587600000,
value: 7390
},
{
instant: 1495591200000,
value: -5670
},
//...
}
Si je regarde l’exemple de https://www.highcharts.com/demo/area-stacked je peux écrire
function _createSeries() {
return MEASURES.map((serie) => {
return {
type: 'area',
name: serie.source,
data: serie.measures.map((measure) => {
return {x: measure.instant, y: measure.value}
})
}
});
}
Highcharts.setOptions({
title: {text: 'Stack area in Highchart'},
xAxis: {
type: 'datetime',
},
plotOptions: {
area: {
stacking: 'normal',
}
}
});
new Highcharts.Chart({chart: {renderTo: 'myChart'}, series: _createSeries()})
Si les valeurs sont toutes positives ou toutes négatives vous n’avez aucun problème. Par contre si vous avez un mix, Highcharts n’est pas capable d’afficher correctement les données comme nous pouvons le voir ci dessous.
Pour contourner le problème nous allons n’afficher que des courbes qui contiennent que des valeurs positives ou que des valeurs positives. Pour chaque jeu de données, chaque série du graphe nous allons en créer deux
une avec des valeurs positives et des 0 à la place des valeurs négatives
une avec des valeurs négatives et des 0 à la place des valeurs positives
function evaluator(measure) {
if (measure.value > 0) {
return positive ? measure.value : 0;
}
return positive ? 0 : measure.value;
}
function _createSeries() {
return MEASURES.map(serie => [
_createSerie(serie, color, true),
_createSerie(serie, color, false)
])
.reduce((acc, val) => acc.concat(val));
}
function _createSerie(serie, positive) {
return {
type: 'area',
stack: positive ? 1 : 0,
showInLegend: positive,
name: serie.source,
data: serie.measures.map((measure) => {
return {x: measure.instant, y: positive !== null ? evaluator(measure) : measure.value}
})
}
}
Pour éviter d’avoir une double légende nous utilisons la propriété showInLegend: positive
. Plus important pour avoir un graphique valable, nous devons indiquer à Highcharts que nous avons 2 manières d’empiler les données (une pour les valeurs positives et une pour les négatives). Nous l’indiquons avec la propriété stack: positive ? 1 : 0
. Les valeurs sont peut importantes elles doivent juste être distinctes dans les 2 cas.
Nous optenons
Est ce mieux ? Les valeurs positives et négatives sont maintenant justes en cumulées. Mais si nous zoomon et regardons par exemple le troisième pas de temps et notamment les enchaînnements entre les points :
Le dessin du graphe est faux quand nous passons d’une valeur positive à négative. Si nous n’affichons qu’une série nous pouvons voir le problème
Comme nous faisons une référence à 0 le tracé est faux.
Il n’y a pas de solution idéale. Si vous voulez des zones empilées le tracé de courbe sera faux. Visuellement les défauts sont atténués si vous n’avez pas de grandes variations des valeurs et si ces valeurs sont nombreuses.
L’autre solution est de ne pas utiliser ce type de graphique si vous voulez cumuler des valeurs positives et négatives. Vous pouvez par exemple utiliser le type column