@hestia-earth/ui-components-charts 0.43.23 → 0.43.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hierarchy-chart.css +45 -0
- package/dist/index.d.mts +666 -65
- package/dist/index.d.ts +666 -65
- package/dist/index.js +1509 -60
- package/dist/index.mjs +1460 -62
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -3,12 +3,28 @@
|
|
|
3
3
|
var utils = require('@hestia-earth/utils');
|
|
4
4
|
var Gradient = require('javascript-color-gradient');
|
|
5
5
|
var shadeGenerator = require('shade-generator/dist/shadeGenerator');
|
|
6
|
+
var d3Selection = require('d3-selection');
|
|
7
|
+
var merge = require('lodash.merge');
|
|
6
8
|
var emissionGroupings = require('@hestia-earth/glossary/resources/emission-groupings.json');
|
|
9
|
+
var chart_js = require('chart.js');
|
|
10
|
+
var C2S = require('canvas-to-svg');
|
|
11
|
+
var schema = require('@hestia-earth/schema');
|
|
12
|
+
var defaultModelIds = require('@hestia-earth/glossary/resources/defaultModelId.json');
|
|
13
|
+
var d3Shape = require('d3-shape');
|
|
14
|
+
require('d3-transition');
|
|
15
|
+
var d3Hierarchy = require('d3-hierarchy');
|
|
16
|
+
var d3Ease = require('d3-ease');
|
|
17
|
+
require('chartjs-adapter-date-fns');
|
|
18
|
+
var annotationPlugin = require('chartjs-plugin-annotation');
|
|
7
19
|
|
|
8
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
21
|
|
|
10
22
|
var Gradient__default = /*#__PURE__*/_interopDefault(Gradient);
|
|
23
|
+
var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
11
24
|
var emissionGroupings__default = /*#__PURE__*/_interopDefault(emissionGroupings);
|
|
25
|
+
var C2S__default = /*#__PURE__*/_interopDefault(C2S);
|
|
26
|
+
var defaultModelIds__default = /*#__PURE__*/_interopDefault(defaultModelIds);
|
|
27
|
+
var annotationPlugin__default = /*#__PURE__*/_interopDefault(annotationPlugin);
|
|
12
28
|
|
|
13
29
|
// src/charts/bar.ts
|
|
14
30
|
|
|
@@ -18,6 +34,17 @@ var defaultTicksFont = {
|
|
|
18
34
|
size: 13
|
|
19
35
|
};
|
|
20
36
|
var chartLabelMaxLength = 50;
|
|
37
|
+
var defaultChartSettings = Object.freeze({
|
|
38
|
+
options: {
|
|
39
|
+
responsive: true,
|
|
40
|
+
maintainAspectRatio: false,
|
|
41
|
+
plugins: {
|
|
42
|
+
legend: {
|
|
43
|
+
display: false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
21
48
|
|
|
22
49
|
// src/charts/bar.ts
|
|
23
50
|
var grey = "#4a4a4a";
|
|
@@ -151,20 +178,20 @@ var palette = Object.freeze({
|
|
|
151
178
|
]
|
|
152
179
|
});
|
|
153
180
|
var getGradients = (...colours) => new Gradient__default.default().setColorGradient(...colours).setMidpoint(10).getColors();
|
|
154
|
-
var getColorGradient = (index,
|
|
155
|
-
const start =
|
|
156
|
-
const end =
|
|
157
|
-
const gradientIndex = Math.ceil(index /
|
|
181
|
+
var getColorGradient = (index, colors2) => {
|
|
182
|
+
const start = colors2[index % colors2.length];
|
|
183
|
+
const end = colors2[index % colors2.length + 1];
|
|
184
|
+
const gradientIndex = Math.ceil(index / colors2.length);
|
|
158
185
|
return getGradients(start, end)[gradientIndex];
|
|
159
186
|
};
|
|
160
|
-
var getColorShade = (index,
|
|
161
|
-
const color =
|
|
162
|
-
const shade = Math.min(100, Math.max(10, Math.abs(100 - Math.ceil(index /
|
|
187
|
+
var getColorShade = (index, colors2) => {
|
|
188
|
+
const color = colors2[index % colors2.length];
|
|
189
|
+
const shade = Math.min(100, Math.max(10, Math.abs(100 - Math.ceil(index / colors2.length) * 10)));
|
|
163
190
|
return shadeGenerator.ShadeGenerator.hue(color).shade(`${shade}`).hex();
|
|
164
191
|
};
|
|
165
192
|
var getColor = (index, scheme = 0 /* categorial */) => {
|
|
166
|
-
const
|
|
167
|
-
return index <
|
|
193
|
+
const colors2 = palette[scheme];
|
|
194
|
+
return index < colors2.length ? colors2[index] : scheme === 0 /* categorial */ ? getColorShade(index, colors2) : getColorGradient(index, colors2);
|
|
168
195
|
};
|
|
169
196
|
var listColor = (_v, index) => getColor(index);
|
|
170
197
|
var listColorContinuous = (_v, index) => getColor(index, 1 /* continuous */);
|
|
@@ -225,48 +252,12 @@ var groupDataByCategory = (data2, labels, category, { emissionCategoryValue: emi
|
|
|
225
252
|
// src/charts/csv.ts
|
|
226
253
|
var escape = (value) => `${value}`.includes(",") ? `"${value}"` : value;
|
|
227
254
|
var toLine = (values) => values.map(escape).join(",");
|
|
228
|
-
var data = emissionGroupings__default.default;
|
|
229
|
-
var categoryLabel = (key) => key ? utils.keyToLabel(key.replace(/category/gi, "")) : "Unset";
|
|
230
|
-
var emissionCategories = Object.keys(data).filter((key) => key.toLowerCase().endsWith("category")).map((key) => ({ key, label: categoryLabel(key) }));
|
|
231
|
-
var categoryValues = (category) => data[category];
|
|
232
|
-
var emissionCategoriesOf = (emissionId) => Object.keys(data[emissionId]).filter((key) => !!data[emissionId][key]);
|
|
233
|
-
var emissionCategoryValue = (emissionId, category) => {
|
|
234
|
-
var _a;
|
|
235
|
-
return (_a = data[emissionId]) == null ? void 0 : _a[category];
|
|
236
|
-
};
|
|
237
|
-
var defaultHorizontalBarConfig = Object.freeze({
|
|
238
|
-
options: {
|
|
239
|
-
indexAxis: "y",
|
|
240
|
-
scales: {
|
|
241
|
-
x: {
|
|
242
|
-
grid: {
|
|
243
|
-
drawOnChartArea: false
|
|
244
|
-
},
|
|
245
|
-
ticks: {
|
|
246
|
-
callback: (value) => utils.toPrecision(Number(value), 3)
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
y: {
|
|
250
|
-
grid: {
|
|
251
|
-
drawOnChartArea: false
|
|
252
|
-
},
|
|
253
|
-
ticks: {
|
|
254
|
-
crossAlign: "near"
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
var defaultHorizontalTooltip = ({ label, count, includedItems }) => (includedItems == null ? void 0 : includedItems.length) ? includedItems.map(defaultHorizontalTooltip).join("</br>") : `${label}: ${count}`;
|
|
261
|
-
var rowHeight = 40;
|
|
262
|
-
var padding = 60;
|
|
263
|
-
var horizontalBarHeight = (minHeight, maxHeight, nbRows) => Math.min(Math.max(nbRows * rowHeight + padding, minHeight), maxHeight);
|
|
264
255
|
var placementGap = (placement) => placement === "right" ? 8 : -8;
|
|
265
256
|
var placementX = (x, chart, data2, placement) => placement === "right" ? !Array.isArray(data2) && data2 < 0 ? positionAtZero(chart) : x : x;
|
|
266
257
|
var positionAtZero = (chart) => chart.scales.x.getPixelForValue(0);
|
|
267
258
|
var defaultBarDrawSettings = {
|
|
268
259
|
placement: "right",
|
|
269
|
-
xPosFn: (x, index,
|
|
260
|
+
xPosFn: (x, index, width2, chart, data2, placement) => (utils.isEmpty(data2) ? positionAtZero(chart) : placementX(x, chart, data2, placement)) + placementGap(placement),
|
|
270
261
|
yPosFn: (y) => y + 3,
|
|
271
262
|
colorFn: (m, index, chart, data2) => utils.isUndefined(data2) ? "#b5b5b5" : "#4a4a4a",
|
|
272
263
|
textFn: ({ data: data2 }) => `${data2}`,
|
|
@@ -305,7 +296,7 @@ var afterBarDrawPlugin = (settings) => ({
|
|
|
305
296
|
...defaultBarDrawSettings,
|
|
306
297
|
...settings != null ? settings : {}
|
|
307
298
|
};
|
|
308
|
-
const { ctx, width, height } = chart;
|
|
299
|
+
const { ctx, width: width2, height } = chart;
|
|
309
300
|
ctx.save();
|
|
310
301
|
const getStackItems = (index) => chart.data.datasets.map((dataset, dsIndex) => ({ dataset, meta: chart.getDatasetMeta(dsIndex) })).filter(({ meta }) => !meta.hidden).map(({ dataset, meta }) => ({
|
|
311
302
|
element: meta.data[index],
|
|
@@ -331,7 +322,7 @@ var afterBarDrawPlugin = (settings) => ({
|
|
|
331
322
|
var _a2, _b2;
|
|
332
323
|
return (_b2 = (_a2 = chart.data.labels) == null ? void 0 : _a2[index]) != null ? _b2 : "";
|
|
333
324
|
};
|
|
334
|
-
const getXPos = (anchorX, index, dataToPass) => xPosFn(anchorX, index,
|
|
325
|
+
const getXPos = (anchorX, index, dataToPass) => xPosFn(anchorX, index, width2, chart, dataToPass, placement);
|
|
335
326
|
const getYPos = (y, index, dataToPass) => yPosFn(y, index, height, chart, dataToPass, placement);
|
|
336
327
|
const getAnchorX = (minX, maxX) => placement === "left" ? minX : maxX;
|
|
337
328
|
const processIndex = (index) => {
|
|
@@ -414,16 +405,16 @@ var createHoverGradient = (ctx, chartArea, color) => {
|
|
|
414
405
|
return gradient;
|
|
415
406
|
};
|
|
416
407
|
var calculateBarBounds = (element, indexAxis = "y") => {
|
|
417
|
-
const { x, y, base, width, height } = element;
|
|
408
|
+
const { x, y, base, width: width2, height } = element;
|
|
418
409
|
if (indexAxis === "y") {
|
|
419
|
-
const
|
|
420
|
-
const barTop = y -
|
|
421
|
-
const barBottom = y +
|
|
410
|
+
const barHeight2 = height;
|
|
411
|
+
const barTop = y - barHeight2 / 2;
|
|
412
|
+
const barBottom = y + barHeight2 / 2;
|
|
422
413
|
const barLeft = Math.min(base, x);
|
|
423
414
|
const barRight = Math.max(base, x);
|
|
424
|
-
return { barLeft, barRight, barTop, barBottom, thickness:
|
|
415
|
+
return { barLeft, barRight, barTop, barBottom, thickness: barHeight2 };
|
|
425
416
|
} else {
|
|
426
|
-
const barWidth =
|
|
417
|
+
const barWidth = width2;
|
|
427
418
|
const barLeft = x - barWidth / 2;
|
|
428
419
|
const barRight = x + barWidth / 2;
|
|
429
420
|
const barTop = Math.min(base, y);
|
|
@@ -670,13 +661,1389 @@ var lollipopChartPlugin = (settings) => ({
|
|
|
670
661
|
var increaseScaleLimits = (scale, handleMinScaleZero = true) => {
|
|
671
662
|
const range = scale.max - scale.min;
|
|
672
663
|
const safeRange = range === 0 ? Math.abs(scale.max) || 1 : range;
|
|
673
|
-
const
|
|
674
|
-
const padding2 = safeRange *
|
|
664
|
+
const margin2 = 0.1;
|
|
665
|
+
const padding2 = safeRange * margin2;
|
|
675
666
|
scale.min -= handleMinScaleZero && scale.min === 0 ? 0 : padding2;
|
|
676
667
|
scale.max += padding2;
|
|
677
668
|
};
|
|
669
|
+
var parsePrecision = (value, precision = 3) => utils.toPrecision(parseFloat(`${value}`), parseInt(`${precision}`, 10));
|
|
670
|
+
var formatPrecision = (value, precision = 3, enableComma = true) => typeof value !== "boolean" && !utils.isUndefined(value) && utils.isNumber(value) ? `${(enableComma ? utils.toComma : (v) => v)(parsePrecision(value, precision))}`.replace(/\.00$/, "") : value;
|
|
671
|
+
|
|
672
|
+
// src/charts/contribution-chart.ts
|
|
673
|
+
var grey2 = "#4a4a4a";
|
|
674
|
+
var contributionBarHeight = 8;
|
|
675
|
+
var contributionHoverHeight = 10;
|
|
676
|
+
var defaultContributionSettings = Object.freeze({
|
|
677
|
+
type: "bar",
|
|
678
|
+
options: {
|
|
679
|
+
indexAxis: "y",
|
|
680
|
+
responsive: true,
|
|
681
|
+
plugins: {
|
|
682
|
+
tooltip: {
|
|
683
|
+
enabled: false
|
|
684
|
+
},
|
|
685
|
+
legend: {
|
|
686
|
+
display: false
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
scales: {
|
|
690
|
+
x: {
|
|
691
|
+
stacked: true,
|
|
692
|
+
display: true,
|
|
693
|
+
grid: {
|
|
694
|
+
display: true,
|
|
695
|
+
drawOnChartArea: true
|
|
696
|
+
},
|
|
697
|
+
ticks: {
|
|
698
|
+
font: defaultTicksFont,
|
|
699
|
+
callback: (label) => `${formatPrecision(+label)}`
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
y: {
|
|
703
|
+
stacked: true,
|
|
704
|
+
display: true,
|
|
705
|
+
position: "left",
|
|
706
|
+
grid: {
|
|
707
|
+
display: true,
|
|
708
|
+
drawOnChartArea: false,
|
|
709
|
+
drawTicks: true,
|
|
710
|
+
tickLength: 8,
|
|
711
|
+
offset: false
|
|
712
|
+
},
|
|
713
|
+
ticks: {
|
|
714
|
+
color: grey2,
|
|
715
|
+
padding: 4,
|
|
716
|
+
crossAlign: "center",
|
|
717
|
+
font: {
|
|
718
|
+
...defaultTicksFont,
|
|
719
|
+
weight: 400
|
|
720
|
+
},
|
|
721
|
+
callback: function(value) {
|
|
722
|
+
const label = this.getLabelForValue(value);
|
|
723
|
+
return utils.ellipsis(label, chartLabelMaxLength);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
var contributionChartHeight = (minHeight, maxHeight, nbRows) => {
|
|
731
|
+
const rowHeight2 = contributionBarHeight + contributionHoverHeight * 2;
|
|
732
|
+
const padding2 = 60;
|
|
733
|
+
return Math.min(Math.max(nbRows * rowHeight2 + padding2, minHeight), maxHeight);
|
|
734
|
+
};
|
|
735
|
+
var datasetCenter = (chart, index) => {
|
|
736
|
+
const elements = chart.data.datasets.map((_, i) => {
|
|
737
|
+
const meta = chart.getDatasetMeta(i);
|
|
738
|
+
return meta.hidden ? null : meta.data[index];
|
|
739
|
+
}).filter((e) => !!e);
|
|
740
|
+
const right = Math.max(...elements.map((e) => e.x), ...elements.map((e) => e.base));
|
|
741
|
+
const left = Math.min(...elements.map((e) => e.x), ...elements.map((e) => e.base));
|
|
742
|
+
return (right + left) / 2;
|
|
743
|
+
};
|
|
744
|
+
var contributionItemColor = (item) => item.backgroundColor || item.borderColor || item.color;
|
|
745
|
+
var dataAtIndex = (data2, index, includeItems = true) => data2.map((item) => {
|
|
746
|
+
var _a;
|
|
747
|
+
return {
|
|
748
|
+
...item,
|
|
749
|
+
value: item.values[index],
|
|
750
|
+
includedItems: includeItems ? (_a = item.includedItems) == null ? void 0 : _a.map((included) => ({
|
|
751
|
+
...included,
|
|
752
|
+
value: included.values[index]
|
|
753
|
+
})) : []
|
|
754
|
+
};
|
|
755
|
+
}).filter((v) => !utils.isUndefined(v.value)).sort((a, b) => Math.abs(b.value) - Math.abs(a.value));
|
|
756
|
+
var contributionCsv = (labels, data2) => [
|
|
757
|
+
toLine(["@id", ...data2.map((v) => v.label)]),
|
|
758
|
+
labels.map((label, index) => toLine([label, ...dataAtIndex(data2, index, false).map((v) => v.value)])).join("\n")
|
|
759
|
+
].join("\n");
|
|
760
|
+
var hasNegativeValues = (data2 = []) => data2.some((value) => value.values.some((v) => v < 0));
|
|
761
|
+
var contributionData = (data2, labels) => ({
|
|
762
|
+
datasets: data2.map(
|
|
763
|
+
(item) => ({
|
|
764
|
+
label: item.label,
|
|
765
|
+
data: item.values,
|
|
766
|
+
backgroundColor: item.backgroundColor || item.color,
|
|
767
|
+
borderColor: item.borderColor || item.color,
|
|
768
|
+
barThickness: contributionBarHeight,
|
|
769
|
+
maxBarThickness: contributionBarHeight,
|
|
770
|
+
includedItems: item.includedItems
|
|
771
|
+
})
|
|
772
|
+
),
|
|
773
|
+
labels
|
|
774
|
+
});
|
|
775
|
+
var contributionChartConfig = ({
|
|
776
|
+
onClick,
|
|
777
|
+
axis,
|
|
778
|
+
label,
|
|
779
|
+
hasNegativeValues: negative,
|
|
780
|
+
mobile
|
|
781
|
+
}) => ({
|
|
782
|
+
options: {
|
|
783
|
+
onClick,
|
|
784
|
+
plugins: { axisHover: axis },
|
|
785
|
+
scales: {
|
|
786
|
+
x: {
|
|
787
|
+
display: !mobile,
|
|
788
|
+
afterDataLimits: (scale) => increaseScaleLimits(scale),
|
|
789
|
+
title: {
|
|
790
|
+
display: !!label,
|
|
791
|
+
text: label
|
|
792
|
+
},
|
|
793
|
+
grid: {
|
|
794
|
+
lineWidth: (context) => context.tick.value === 0 && negative ? 1 : 0
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
y: {
|
|
798
|
+
display: !mobile
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
plugins: [
|
|
803
|
+
backgroundHoverPlugin({ threshold: contributionHoverHeight }),
|
|
804
|
+
afterBarDrawPlugin({
|
|
805
|
+
placement: "right",
|
|
806
|
+
textFn: ({ data: data2 }) => utils.isUndefined(data2) ? "No data" : ""
|
|
807
|
+
}),
|
|
808
|
+
...axis ? [axisHoverPlugin] : []
|
|
809
|
+
]
|
|
810
|
+
});
|
|
811
|
+
var d3ellipse = (selection, maxWidth, ellipsisStr = "...") => {
|
|
812
|
+
selection.each(function() {
|
|
813
|
+
const t = d3Selection.select(this);
|
|
814
|
+
if (t.node().getComputedTextLength() > maxWidth) {
|
|
815
|
+
const chars = t.text().split("");
|
|
816
|
+
const line2 = [];
|
|
817
|
+
do {
|
|
818
|
+
line2.push(chars.shift());
|
|
819
|
+
t.text(line2.join("") + ellipsisStr);
|
|
820
|
+
} while (t.node().getComputedTextLength() <= maxWidth);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
};
|
|
824
|
+
var shouldBreakLine = (overflows, isMultiWordLine, isOwnLineWord, isUnderLineLimit) => (overflows && isMultiWordLine || isOwnLineWord) && isUnderLineLimit;
|
|
825
|
+
var d3wrap = (t, {
|
|
826
|
+
maxWidth,
|
|
827
|
+
maxLines,
|
|
828
|
+
words,
|
|
829
|
+
nHeight,
|
|
830
|
+
lHeight,
|
|
831
|
+
vcentre = false,
|
|
832
|
+
ownLineWord = ""
|
|
833
|
+
}) => {
|
|
834
|
+
words = words.reverse();
|
|
835
|
+
const y = t.attr("y");
|
|
836
|
+
const x = t.attr("x");
|
|
837
|
+
let lineNumber = 1;
|
|
838
|
+
let word;
|
|
839
|
+
let line2 = [];
|
|
840
|
+
let tspan = t.text(null).append("tspan").attr("x", x).attr("y", y);
|
|
841
|
+
while ((word = words.pop()) && lineNumber <= maxLines) {
|
|
842
|
+
line2.push(word);
|
|
843
|
+
tspan.text(line2.join(" "));
|
|
844
|
+
if (shouldBreakLine(
|
|
845
|
+
tspan.node().getComputedTextLength() > maxWidth,
|
|
846
|
+
line2.length > 1,
|
|
847
|
+
word === ownLineWord,
|
|
848
|
+
lineNumber < maxLines
|
|
849
|
+
)) {
|
|
850
|
+
line2.pop();
|
|
851
|
+
tspan.text(line2.join(" "));
|
|
852
|
+
line2 = [word];
|
|
853
|
+
tspan = t.append("tspan").attr("x", x).attr("y", y).attr("dy", lineNumber++ * lHeight + "px").text(word);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
d3ellipse(tspan, maxWidth, "... " + ownLineWord);
|
|
857
|
+
if (vcentre) {
|
|
858
|
+
const yOffset = nHeight / 2 - (t.selectAll("tspan").size() - 1) * (lHeight / 2);
|
|
859
|
+
t.selectAll("tspan").attr("y", yOffset);
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
var colors = {
|
|
863
|
+
darkBlue: "rgba(25, 57, 87, 1)",
|
|
864
|
+
lightBlue: "rgba(132, 160, 220, 1)",
|
|
865
|
+
// `$warning` — marks the reported value, and is the one colour that reads against both a white
|
|
866
|
+
// page and the dark blue popover the parametric chart is shown in
|
|
867
|
+
orange: "rgba(255, 136, 27, 1)"
|
|
868
|
+
};
|
|
869
|
+
var opaqueColor = (color, opacity = 0.5) => color.replace(", 1)", `, ${opacity})`);
|
|
870
|
+
var defaultDistributionSettings = Object.freeze({
|
|
871
|
+
type: "line",
|
|
872
|
+
options: {
|
|
873
|
+
events: [],
|
|
874
|
+
responsive: true,
|
|
875
|
+
plugins: {
|
|
876
|
+
tooltip: {
|
|
877
|
+
enabled: false
|
|
878
|
+
},
|
|
879
|
+
legend: {
|
|
880
|
+
display: false,
|
|
881
|
+
align: "center"
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
scales: {
|
|
885
|
+
y: {
|
|
886
|
+
display: true,
|
|
887
|
+
position: "left",
|
|
888
|
+
grid: {
|
|
889
|
+
display: true
|
|
890
|
+
},
|
|
891
|
+
beginAtZero: true,
|
|
892
|
+
ticks: {
|
|
893
|
+
crossAlign: "center"
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
var joinXLabel = " to ";
|
|
900
|
+
var createDynamicHistogramData = (values, numberOfBins) => {
|
|
901
|
+
if (values.length === 0) {
|
|
902
|
+
return { labels: [], frequencies: [] };
|
|
903
|
+
}
|
|
904
|
+
const min = Math.min(...values);
|
|
905
|
+
const max = Math.max(...values);
|
|
906
|
+
const binSize = (max - min) / numberOfBins;
|
|
907
|
+
const frequencies = new Array(numberOfBins).fill(0);
|
|
908
|
+
const labels = [];
|
|
909
|
+
for (let i = 0; i < numberOfBins; i++) {
|
|
910
|
+
const binStart = min + i * binSize;
|
|
911
|
+
const binEnd = binStart + binSize;
|
|
912
|
+
labels.push([binStart, binEnd].join(joinXLabel));
|
|
913
|
+
values.forEach((point) => {
|
|
914
|
+
if (point >= binStart && point < binEnd) {
|
|
915
|
+
frequencies[i]++;
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
frequencies[numberOfBins - 1] += values.filter((p) => p === max).length;
|
|
920
|
+
return { labels, frequencies };
|
|
921
|
+
};
|
|
922
|
+
var curveSpread = 4;
|
|
923
|
+
var curvePoints = 120;
|
|
924
|
+
var curvePadding = 0.05;
|
|
925
|
+
var normalPdf = (x, mu, sd) => Math.exp(-0.5 * ((x - mu) / sd) ** 2) / (sd * Math.sqrt(2 * Math.PI));
|
|
926
|
+
var curveRange = (mu, sd, value, minX) => {
|
|
927
|
+
const values = [mu - curveSpread * sd, mu + curveSpread * sd, ...Number.isFinite(value) ? [value] : []];
|
|
928
|
+
const from = Math.min(...values);
|
|
929
|
+
const to = Math.max(...values);
|
|
930
|
+
const padding2 = (to - from) * curvePadding;
|
|
931
|
+
return { from: Number.isFinite(minX) ? Math.max(from - padding2, minX) : from - padding2, to: to + padding2 };
|
|
932
|
+
};
|
|
933
|
+
var createNormalCurve = (mu, sd, value, minX) => {
|
|
934
|
+
const { from, to } = curveRange(mu, sd, value, minX);
|
|
935
|
+
return Array.from({ length: curvePoints + 1 }, (_, index) => from + (to - from) * index / curvePoints).map((x) => ({
|
|
936
|
+
x,
|
|
937
|
+
y: normalPdf(x, mu, sd)
|
|
938
|
+
}));
|
|
939
|
+
};
|
|
940
|
+
var distributionAnnotations = (min, max, value, mean) => ({
|
|
941
|
+
...Number.isFinite(min) && Number.isFinite(max) ? {
|
|
942
|
+
interval: {
|
|
943
|
+
type: "box",
|
|
944
|
+
xMin: min,
|
|
945
|
+
xMax: max,
|
|
946
|
+
backgroundColor: opaqueColor(colors.lightBlue, 0.3),
|
|
947
|
+
borderWidth: 0,
|
|
948
|
+
drawTime: "beforeDatasetsDraw"
|
|
949
|
+
}
|
|
950
|
+
} : {},
|
|
951
|
+
...Number.isFinite(mean) ? {
|
|
952
|
+
mean: {
|
|
953
|
+
type: "line",
|
|
954
|
+
xMin: mean,
|
|
955
|
+
xMax: mean,
|
|
956
|
+
borderColor: colors.lightBlue,
|
|
957
|
+
borderWidth: 1,
|
|
958
|
+
borderDash: [4, 4],
|
|
959
|
+
label: {
|
|
960
|
+
display: true,
|
|
961
|
+
content: "average",
|
|
962
|
+
position: "start",
|
|
963
|
+
backgroundColor: colors.lightBlue,
|
|
964
|
+
color: colors.darkBlue,
|
|
965
|
+
font: { size: 9 }
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
} : {},
|
|
969
|
+
...Number.isFinite(value) ? {
|
|
970
|
+
value: {
|
|
971
|
+
type: "line",
|
|
972
|
+
xMin: value,
|
|
973
|
+
xMax: value,
|
|
974
|
+
borderColor: colors.orange,
|
|
975
|
+
borderWidth: 2,
|
|
976
|
+
label: {
|
|
977
|
+
display: true,
|
|
978
|
+
content: `${formatPrecision(value, 3, true)}`,
|
|
979
|
+
position: "start",
|
|
980
|
+
backgroundColor: colors.orange,
|
|
981
|
+
color: "#fff",
|
|
982
|
+
font: { size: 10 }
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
} : {}
|
|
986
|
+
});
|
|
987
|
+
var createSinglePointDataset = (labels, pointValue) => {
|
|
988
|
+
const pointData = new Array(labels.length).fill(null);
|
|
989
|
+
const binIndex = labels.findIndex((label) => {
|
|
990
|
+
const [minStr, maxStr] = label.split(joinXLabel);
|
|
991
|
+
const binMin = parseFloat(minStr);
|
|
992
|
+
const binMax = parseFloat(maxStr);
|
|
993
|
+
return pointValue >= binMin && pointValue < binMax;
|
|
994
|
+
});
|
|
995
|
+
if (binIndex !== -1) {
|
|
996
|
+
pointData[binIndex] = 1;
|
|
997
|
+
}
|
|
998
|
+
return pointData;
|
|
999
|
+
};
|
|
1000
|
+
var histogramConfig = (label) => ({
|
|
1001
|
+
options: {
|
|
1002
|
+
scales: {
|
|
1003
|
+
x: {
|
|
1004
|
+
display: true,
|
|
1005
|
+
grid: {
|
|
1006
|
+
offset: true
|
|
1007
|
+
},
|
|
1008
|
+
min: 0,
|
|
1009
|
+
ticks: {
|
|
1010
|
+
callback: function(value) {
|
|
1011
|
+
var _a, _b, _c;
|
|
1012
|
+
const tickLabel = this.getLabelForValue(value);
|
|
1013
|
+
return (_c = (_b = (_a = tickLabel == null ? void 0 : tickLabel.split(joinXLabel)) == null ? void 0 : _a.map(parseFloat)) == null ? void 0 : _b.map((v) => formatPrecision(v, 3, true))) == null ? void 0 : _c.join("-");
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
title: {
|
|
1017
|
+
display: true,
|
|
1018
|
+
text: label
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
y: {
|
|
1022
|
+
ticks: {
|
|
1023
|
+
font: {
|
|
1024
|
+
family: defaultTicksFont.family,
|
|
1025
|
+
size: 10,
|
|
1026
|
+
weight: 400
|
|
1027
|
+
},
|
|
1028
|
+
color: "#4A4A4A"
|
|
1029
|
+
},
|
|
1030
|
+
title: {
|
|
1031
|
+
display: true,
|
|
1032
|
+
text: "Frequency of values"
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
var parametricConfig = ({ label, intervalMin, intervalMax, value, mu }) => ({
|
|
1039
|
+
options: {
|
|
1040
|
+
plugins: {
|
|
1041
|
+
annotation: {
|
|
1042
|
+
annotations: distributionAnnotations(intervalMin, intervalMax, value, mu)
|
|
1043
|
+
}
|
|
1044
|
+
},
|
|
1045
|
+
scales: {
|
|
1046
|
+
x: {
|
|
1047
|
+
type: "linear",
|
|
1048
|
+
display: true,
|
|
1049
|
+
ticks: {
|
|
1050
|
+
font: {
|
|
1051
|
+
family: defaultTicksFont.family,
|
|
1052
|
+
size: 10,
|
|
1053
|
+
weight: 400
|
|
1054
|
+
},
|
|
1055
|
+
color: "#4A4A4A",
|
|
1056
|
+
callback: (tick) => `${formatPrecision(tick, 3, true)}`
|
|
1057
|
+
},
|
|
1058
|
+
title: {
|
|
1059
|
+
display: true,
|
|
1060
|
+
text: label
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
1063
|
+
y: {
|
|
1064
|
+
// the density itself carries no meaning for the reader, only the shape does
|
|
1065
|
+
ticks: {
|
|
1066
|
+
display: false
|
|
1067
|
+
},
|
|
1068
|
+
grid: {
|
|
1069
|
+
display: false
|
|
1070
|
+
},
|
|
1071
|
+
title: {
|
|
1072
|
+
display: false
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
var withinPercentile = (distribution = [], maxPercentile = 0) => {
|
|
1079
|
+
const limit = maxPercentile ? utils.getPercentileValue(distribution, maxPercentile) : null;
|
|
1080
|
+
return limit ? distribution.filter((v) => v <= limit) : distribution;
|
|
1081
|
+
};
|
|
1082
|
+
var histogramDatasets = (frequencies) => [
|
|
1083
|
+
{
|
|
1084
|
+
label: "Distribution",
|
|
1085
|
+
data: frequencies,
|
|
1086
|
+
backgroundColor: opaqueColor(colors.lightBlue),
|
|
1087
|
+
borderColor: colors.lightBlue,
|
|
1088
|
+
borderWidth: 1,
|
|
1089
|
+
type: "bar"
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
label: "Distribution",
|
|
1093
|
+
data: frequencies,
|
|
1094
|
+
backgroundColor: opaqueColor(colors.darkBlue),
|
|
1095
|
+
borderColor: colors.darkBlue,
|
|
1096
|
+
borderWidth: 1,
|
|
1097
|
+
fill: false,
|
|
1098
|
+
pointRadius: 0,
|
|
1099
|
+
type: "line"
|
|
1100
|
+
}
|
|
1101
|
+
];
|
|
1102
|
+
var valueDataset = (singlePoint) => (singlePoint == null ? void 0 : singlePoint.length) ? [
|
|
1103
|
+
{
|
|
1104
|
+
label: "Value",
|
|
1105
|
+
data: singlePoint,
|
|
1106
|
+
fill: false,
|
|
1107
|
+
borderColor: colors.darkBlue,
|
|
1108
|
+
backgroundColor: colors.darkBlue,
|
|
1109
|
+
pointRadius: 4,
|
|
1110
|
+
showLine: false,
|
|
1111
|
+
type: "line",
|
|
1112
|
+
tension: 0
|
|
1113
|
+
}
|
|
1114
|
+
] : [];
|
|
1115
|
+
var histogramData = ({ distribution, value, nbBins = 10, maxPercentile = 0 }) => {
|
|
1116
|
+
const grouped = createDynamicHistogramData(withinPercentile(distribution, maxPercentile), nbBins);
|
|
1117
|
+
return {
|
|
1118
|
+
datasets: [
|
|
1119
|
+
...distribution ? histogramDatasets(grouped.frequencies) : [],
|
|
1120
|
+
...valueDataset(createSinglePointDataset((grouped == null ? void 0 : grouped.labels) || [], value))
|
|
1121
|
+
],
|
|
1122
|
+
labels: grouped.labels
|
|
1123
|
+
};
|
|
1124
|
+
};
|
|
1125
|
+
var parametricData = ({ mu, sd, value, minX }) => ({
|
|
1126
|
+
datasets: [
|
|
1127
|
+
{
|
|
1128
|
+
label: "Distribution",
|
|
1129
|
+
data: createNormalCurve(mu, sd, value, minX),
|
|
1130
|
+
backgroundColor: opaqueColor(colors.lightBlue, 0.2),
|
|
1131
|
+
// light enough to read on a dark panel, dark enough to read on a white one
|
|
1132
|
+
borderColor: colors.lightBlue,
|
|
1133
|
+
borderWidth: 1,
|
|
1134
|
+
fill: true,
|
|
1135
|
+
pointRadius: 0,
|
|
1136
|
+
type: "line"
|
|
1137
|
+
}
|
|
1138
|
+
]
|
|
1139
|
+
});
|
|
1140
|
+
var isParametric = ({ mu, sd }) => Number.isFinite(mu) && Number.isFinite(sd) && sd > 0;
|
|
1141
|
+
var distributionChart = (inputs) => {
|
|
1142
|
+
var _a;
|
|
1143
|
+
const label = (_a = inputs.label) != null ? _a : "Distribution";
|
|
1144
|
+
return isParametric(inputs) ? {
|
|
1145
|
+
data: parametricData(inputs),
|
|
1146
|
+
config: merge__default.default({}, defaultDistributionSettings, parametricConfig({ ...inputs, label }))
|
|
1147
|
+
} : {
|
|
1148
|
+
data: histogramData(inputs),
|
|
1149
|
+
config: merge__default.default({}, defaultDistributionSettings, histogramConfig(label))
|
|
1150
|
+
};
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
// src/charts/download.ts
|
|
1154
|
+
var downloadFile = (url, fileName) => {
|
|
1155
|
+
if (!url) {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
const a = document.createElement("a");
|
|
1159
|
+
document.body.appendChild(a);
|
|
1160
|
+
a.setAttribute("style", "display: none");
|
|
1161
|
+
a.href = url;
|
|
1162
|
+
a.download = fileName;
|
|
1163
|
+
a.click();
|
|
1164
|
+
a.remove();
|
|
1165
|
+
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
1166
|
+
};
|
|
1167
|
+
var data = emissionGroupings__default.default;
|
|
1168
|
+
var categoryLabel = (key) => key ? utils.keyToLabel(key.replace(/category/gi, "")) : "Unset";
|
|
1169
|
+
var emissionCategories = Object.keys(data).filter((key) => key.toLowerCase().endsWith("category")).map((key) => ({ key, label: categoryLabel(key) }));
|
|
1170
|
+
var categoryValues = (category) => data[category];
|
|
1171
|
+
var emissionCategoriesOf = (emissionId) => Object.keys(data[emissionId]).filter((key) => !!data[emissionId][key]);
|
|
1172
|
+
var emissionCategoryValue = (emissionId, category) => {
|
|
1173
|
+
var _a;
|
|
1174
|
+
return (_a = data[emissionId]) == null ? void 0 : _a[category];
|
|
1175
|
+
};
|
|
1176
|
+
var defaultChartWidth = 800;
|
|
1177
|
+
var defaultChartHeight = 600;
|
|
1178
|
+
var extractEssentialChartConfig = (config) => {
|
|
1179
|
+
var _a, _b;
|
|
1180
|
+
return {
|
|
1181
|
+
type: config.type || "bar",
|
|
1182
|
+
data: config.data,
|
|
1183
|
+
options: {
|
|
1184
|
+
indexAxis: (_a = config.options) == null ? void 0 : _a.indexAxis,
|
|
1185
|
+
responsive: false,
|
|
1186
|
+
maintainAspectRatio: false,
|
|
1187
|
+
devicePixelRatio: 1,
|
|
1188
|
+
animation: false,
|
|
1189
|
+
animations: {
|
|
1190
|
+
colors: false,
|
|
1191
|
+
x: false,
|
|
1192
|
+
y: false
|
|
1193
|
+
},
|
|
1194
|
+
transitions: {
|
|
1195
|
+
active: {
|
|
1196
|
+
animation: {
|
|
1197
|
+
duration: 0
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
plugins: {
|
|
1202
|
+
legend: { display: false },
|
|
1203
|
+
title: ((_b = config.options.plugins) == null ? void 0 : _b.title) || { display: false }
|
|
1204
|
+
},
|
|
1205
|
+
scales: config.options.scales
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
};
|
|
1209
|
+
var stretchSvg = (svgString, width2, height) => {
|
|
1210
|
+
const parser = new DOMParser();
|
|
1211
|
+
const doc = parser.parseFromString(svgString, "image/svg+xml");
|
|
1212
|
+
const svgElement = doc.documentElement;
|
|
1213
|
+
svgElement.setAttribute("viewBox", `0 0 ${width2 + 100} ${height}`);
|
|
1214
|
+
svgElement.setAttribute("width", "100%");
|
|
1215
|
+
svgElement.setAttribute("height", "100%");
|
|
1216
|
+
svgElement.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
1217
|
+
return svgElement.outerHTML;
|
|
1218
|
+
};
|
|
1219
|
+
var parseAfterDrawBarPlugins = (afterBarDrawConfig, units) => Array.isArray(afterBarDrawConfig) ? afterBarDrawConfig.map(afterBarDrawPlugin) : [
|
|
1220
|
+
afterBarDrawPlugin({
|
|
1221
|
+
textFn: ({ data: data2 }) => [utils.toPrecision(Array.isArray(data2) ? data2[0] : data2), units].filter(Boolean).join(" "),
|
|
1222
|
+
emptyValueLabel: "No data",
|
|
1223
|
+
...afterBarDrawConfig || {}
|
|
1224
|
+
})
|
|
1225
|
+
];
|
|
1226
|
+
var convertToSvg = (config, metadata = {}) => new Promise((resolve, reject) => {
|
|
1227
|
+
const width2 = metadata.width || defaultChartWidth;
|
|
1228
|
+
const height = metadata.height || defaultChartHeight;
|
|
1229
|
+
const svgContext = new C2S__default.default(width2, height);
|
|
1230
|
+
svgContext.setTransform = () => {
|
|
1231
|
+
};
|
|
1232
|
+
svgContext.resetTransform = () => {
|
|
1233
|
+
};
|
|
1234
|
+
svgContext.roundRect = function(x, y, w, h) {
|
|
1235
|
+
this.rect(x, y, w, h);
|
|
1236
|
+
};
|
|
1237
|
+
const mockCanvas = document.createElement("canvas");
|
|
1238
|
+
mockCanvas.width = width2;
|
|
1239
|
+
mockCanvas.height = height;
|
|
1240
|
+
mockCanvas.style.width = `${width2}px`;
|
|
1241
|
+
mockCanvas.style.height = `${height}px`;
|
|
1242
|
+
mockCanvas.getBoundingClientRect = () => ({
|
|
1243
|
+
x: 0,
|
|
1244
|
+
y: 0,
|
|
1245
|
+
bottom: height,
|
|
1246
|
+
height,
|
|
1247
|
+
left: 0,
|
|
1248
|
+
right: width2,
|
|
1249
|
+
top: 0,
|
|
1250
|
+
width: width2,
|
|
1251
|
+
toJSON: () => {
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
mockCanvas.getContext = (type) => {
|
|
1255
|
+
if (type === "2d") {
|
|
1256
|
+
svgContext.canvas = mockCanvas;
|
|
1257
|
+
return svgContext;
|
|
1258
|
+
}
|
|
1259
|
+
return null;
|
|
1260
|
+
};
|
|
1261
|
+
const chartConfig = extractEssentialChartConfig(config);
|
|
1262
|
+
let chart;
|
|
1263
|
+
chartConfig.options.animation = {
|
|
1264
|
+
onComplete: () => {
|
|
1265
|
+
try {
|
|
1266
|
+
const svgString = svgContext.getSerializedSvg(true);
|
|
1267
|
+
const finalSvg = stretchSvg(svgString, width2, height);
|
|
1268
|
+
chart.destroy();
|
|
1269
|
+
resolve(finalSvg);
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
reject(e);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
chartConfig.plugins = [
|
|
1276
|
+
metadata.afterBarDrawConfig !== null && parseAfterDrawBarPlugins(metadata.afterBarDrawConfig, metadata.units),
|
|
1277
|
+
metadata.lollipopConfig !== null && lollipopChartPlugin(metadata.lollipopConfig)
|
|
1278
|
+
].filter(Boolean).flat();
|
|
1279
|
+
chart = new chart_js.Chart(mockCanvas, chartConfig);
|
|
1280
|
+
});
|
|
1281
|
+
var exportAsSVG = async (config, metadata) => {
|
|
1282
|
+
try {
|
|
1283
|
+
const content = await convertToSvg(config, metadata);
|
|
1284
|
+
const blob = new Blob([content], { type: "image/svg+xml;charset=utf-8" });
|
|
1285
|
+
const url = URL.createObjectURL(blob);
|
|
1286
|
+
downloadFile(url, metadata.fileName || "chart-export.svg");
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
console.error("Failed to export SVG", error);
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
var svgToUrl = (svgElement) => {
|
|
1292
|
+
const serializer = new XMLSerializer();
|
|
1293
|
+
let svgString = serializer.serializeToString(svgElement);
|
|
1294
|
+
if (!svgString.includes("xmlns")) {
|
|
1295
|
+
svgString = svgString.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
|
|
1296
|
+
}
|
|
1297
|
+
const blob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" });
|
|
1298
|
+
return URL.createObjectURL(blob);
|
|
1299
|
+
};
|
|
1300
|
+
var downloadSvg = (svgElement) => downloadFile(svgToUrl(svgElement), "chart.svg");
|
|
1301
|
+
var downloadPng = async (svgElement, { width: width2, height } = {}) => {
|
|
1302
|
+
const canvas = document.createElement("canvas");
|
|
1303
|
+
const ctx = canvas.getContext("2d");
|
|
1304
|
+
const img = new Image();
|
|
1305
|
+
const svgSize = svgElement.getBoundingClientRect();
|
|
1306
|
+
canvas.width = width2 || svgSize.width;
|
|
1307
|
+
canvas.height = height || svgSize.height;
|
|
1308
|
+
img.onload = () => {
|
|
1309
|
+
if (ctx) {
|
|
1310
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1311
|
+
ctx.drawImage(img, 0, 0);
|
|
1312
|
+
const pngUrl = canvas.toDataURL("image/png");
|
|
1313
|
+
downloadFile(pngUrl, "chart.png");
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
img.src = svgToUrl(svgElement);
|
|
1317
|
+
};
|
|
1318
|
+
var ChartNodeType = /* @__PURE__ */ ((ChartNodeType2) => {
|
|
1319
|
+
ChartNodeType2["midpoint"] = "midpoint";
|
|
1320
|
+
ChartNodeType2["category"] = "category";
|
|
1321
|
+
ChartNodeType2["emission"] = "emission";
|
|
1322
|
+
ChartNodeType2["endpoint"] = "endpoint";
|
|
1323
|
+
ChartNodeType2["input"] = "input";
|
|
1324
|
+
ChartNodeType2["operation"] = "operation";
|
|
1325
|
+
ChartNodeType2["root"] = "root";
|
|
1326
|
+
return ChartNodeType2;
|
|
1327
|
+
})(ChartNodeType || {});
|
|
1328
|
+
var nodeBorderColours = {
|
|
1329
|
+
["endpoint" /* endpoint */]: "#193957",
|
|
1330
|
+
["midpoint" /* midpoint */]: "#249DA5",
|
|
1331
|
+
["category" /* category */]: "#DC7033",
|
|
1332
|
+
["emission" /* emission */]: "#498F78",
|
|
1333
|
+
["input" /* input */]: "#FFC000",
|
|
1334
|
+
["operation" /* operation */]: "#D0CABA"
|
|
1335
|
+
};
|
|
1336
|
+
var nodeColours = {
|
|
1337
|
+
["endpoint" /* endpoint */]: "#DBE3EA",
|
|
1338
|
+
["midpoint" /* midpoint */]: "#D3EBED",
|
|
1339
|
+
["category" /* category */]: "#FFDEC0",
|
|
1340
|
+
["emission" /* emission */]: "#D5F3D8",
|
|
1341
|
+
["input" /* input */]: "#FFE8A3",
|
|
1342
|
+
["operation" /* operation */]: "#FBF4DF"
|
|
1343
|
+
};
|
|
1344
|
+
var legend = [
|
|
1345
|
+
{ type: "endpoint" /* endpoint */, text: "Endpoint / Single Score" },
|
|
1346
|
+
{ type: "midpoint" /* midpoint */, text: "Characterised Indicator" },
|
|
1347
|
+
{ type: "category" /* category */, text: "Category" },
|
|
1348
|
+
{ type: "emission" /* emission */, text: "Emission / Resource Use" },
|
|
1349
|
+
{ type: "input" /* input */, text: "Input" },
|
|
1350
|
+
{ type: "operation" /* operation */, text: "Operation" }
|
|
1351
|
+
];
|
|
1352
|
+
var getUnitsAndLabel = (terms, fallbackLabel = "unknown term") => {
|
|
1353
|
+
var _a, _b;
|
|
1354
|
+
const allTerms = (Array.isArray(terms) ? terms : [terms]).filter(Boolean);
|
|
1355
|
+
const term2 = allTerms == null ? void 0 : allTerms[0];
|
|
1356
|
+
const label = (_b = (_a = term2 == null ? void 0 : term2.name) != null ? _a : term2 == null ? void 0 : term2["@id"]) != null ? _b : fallbackLabel;
|
|
1357
|
+
const units = (term2 == null ? void 0 : term2.units) ? `(${term2 == null ? void 0 : term2.units})` : "";
|
|
1358
|
+
return {
|
|
1359
|
+
label: `${label.replace(units, "").trim()}${allTerms.length > 1 ? ` (and ${allTerms.length - 1} more)` : ""}`,
|
|
1360
|
+
terms: allTerms,
|
|
1361
|
+
units
|
|
1362
|
+
};
|
|
1363
|
+
};
|
|
1364
|
+
var typeMapping = {
|
|
1365
|
+
[schema.TermTermType.endpointIndicator]: "endpoint" /* endpoint */,
|
|
1366
|
+
[schema.TermTermType.characterisedIndicator]: "midpoint" /* midpoint */,
|
|
1367
|
+
[schema.TermTermType.emission]: "emission" /* emission */,
|
|
1368
|
+
[schema.TermTermType.resourceUse]: "emission" /* emission */,
|
|
1369
|
+
[schema.TermTermType.operation]: "operation" /* operation */
|
|
1370
|
+
};
|
|
1371
|
+
var safeDivide = (numerator, denominator) => denominator !== 0 ? numerator / denominator : 0;
|
|
1372
|
+
var sumIndicators = (data2) => {
|
|
1373
|
+
const totalValues = data2.reduce(
|
|
1374
|
+
(acc, log) => {
|
|
1375
|
+
acc[log.indicator] = acc[log.indicator] || [];
|
|
1376
|
+
!utils.isUndefined(log.weightedValue) && acc[log.indicator].push(log.weightedValue);
|
|
1377
|
+
if (log.category) {
|
|
1378
|
+
const categoryKey = `${log.indicator}-${log.category}`;
|
|
1379
|
+
acc[categoryKey] = acc[categoryKey] || [];
|
|
1380
|
+
!utils.isUndefined(log.weightedValue) && acc[categoryKey].push(log.weightedValue);
|
|
1381
|
+
}
|
|
1382
|
+
return acc;
|
|
1383
|
+
},
|
|
1384
|
+
{}
|
|
1385
|
+
);
|
|
1386
|
+
return Object.fromEntries(
|
|
1387
|
+
Object.entries(totalValues).map(([key, values]) => [
|
|
1388
|
+
key,
|
|
1389
|
+
// if the total is positive, we ignore negative values
|
|
1390
|
+
utils.sum(values) < 0 ? utils.sum(values) : utils.sum(values.filter((v) => v >= 0))
|
|
1391
|
+
])
|
|
1392
|
+
);
|
|
1393
|
+
};
|
|
1394
|
+
var groupIndicators = (data2, terms = {}, totals = {}) => data2.reduce(
|
|
1395
|
+
(acc, log) => {
|
|
1396
|
+
var _a, _b;
|
|
1397
|
+
const contributorType = typeMapping[(_a = terms[log.contributor]) == null ? void 0 : _a.termType] || "input" /* input */;
|
|
1398
|
+
const indicatorType = typeMapping[(_b = terms[log.indicator]) == null ? void 0 : _b.termType];
|
|
1399
|
+
const categoryKey = log.category ? `${log.indicator}-${log.category}` : void 0;
|
|
1400
|
+
const parentTotal = categoryKey ? totals[categoryKey] : totals[log.indicator];
|
|
1401
|
+
const contributorNode = {
|
|
1402
|
+
id: log.contributor,
|
|
1403
|
+
...getUnitsAndLabel(terms[log.contributor], log.contributor),
|
|
1404
|
+
type: contributorType,
|
|
1405
|
+
value: log.value,
|
|
1406
|
+
weightedValue: log.weightedValue,
|
|
1407
|
+
modelId: log.modelId,
|
|
1408
|
+
fraction: safeDivide(log.weightedValue, parentTotal),
|
|
1409
|
+
children: contributorType === "emission" /* emission */ ? log.inputs.map((i) => ({
|
|
1410
|
+
...i,
|
|
1411
|
+
...getUnitsAndLabel(terms[i.id], i.label),
|
|
1412
|
+
type: "input" /* input */,
|
|
1413
|
+
fraction: safeDivide(i.weightedValue, log.value),
|
|
1414
|
+
children: i.operations.map((o) => ({
|
|
1415
|
+
...o,
|
|
1416
|
+
...getUnitsAndLabel(terms[o.id], o.label),
|
|
1417
|
+
type: "operation" /* operation */,
|
|
1418
|
+
fraction: safeDivide(o.weightedValue, i.weightedValue)
|
|
1419
|
+
}))
|
|
1420
|
+
})) : []
|
|
1421
|
+
};
|
|
1422
|
+
acc[log.indicator] = acc[log.indicator] || {
|
|
1423
|
+
id: log.indicator,
|
|
1424
|
+
...getUnitsAndLabel(terms[log.indicator], log.indicator),
|
|
1425
|
+
modelId: log.modelId,
|
|
1426
|
+
type: indicatorType,
|
|
1427
|
+
value: totals[log.indicator],
|
|
1428
|
+
children: []
|
|
1429
|
+
};
|
|
1430
|
+
if (log.category) {
|
|
1431
|
+
const categoryId = `cat-${log.indicator}-${log.category}`;
|
|
1432
|
+
let categoryNode = acc[log.indicator].children.find((c) => c.id === categoryId);
|
|
1433
|
+
if (!categoryNode) {
|
|
1434
|
+
categoryNode = {
|
|
1435
|
+
id: categoryId,
|
|
1436
|
+
label: log.category,
|
|
1437
|
+
type: "category" /* category */,
|
|
1438
|
+
units: "",
|
|
1439
|
+
value: totals[categoryKey],
|
|
1440
|
+
weightedValue: totals[categoryKey],
|
|
1441
|
+
modelId: log.modelId,
|
|
1442
|
+
fraction: safeDivide(totals[categoryKey], totals[log.indicator]),
|
|
1443
|
+
children: []
|
|
1444
|
+
};
|
|
1445
|
+
acc[log.indicator].children.push(categoryNode);
|
|
1446
|
+
}
|
|
1447
|
+
categoryNode.children.push(contributorNode);
|
|
1448
|
+
} else {
|
|
1449
|
+
acc[log.indicator].children.push(contributorNode);
|
|
1450
|
+
}
|
|
1451
|
+
return acc;
|
|
1452
|
+
},
|
|
1453
|
+
{}
|
|
1454
|
+
);
|
|
1455
|
+
|
|
1456
|
+
// src/charts/hierarchy/contributions.ts
|
|
1457
|
+
var hierarchyTermTypes = [
|
|
1458
|
+
schema.TermTermType.resourceUse,
|
|
1459
|
+
schema.TermTermType.emission,
|
|
1460
|
+
schema.TermTermType.characterisedIndicator,
|
|
1461
|
+
schema.TermTermType.endpointIndicator,
|
|
1462
|
+
schema.TermTermType.model
|
|
1463
|
+
];
|
|
1464
|
+
var defaultIndicatorsModelId = "defaultIndicatorsModelId";
|
|
1465
|
+
var defaultIndicatorsModelName = "Default Indicators (multi-model)";
|
|
1466
|
+
var defaultModelId = (indicatorId) => defaultModelIds__default.default[indicatorId];
|
|
1467
|
+
var hierarchyWarnings = (logs, terms) => [!logs.every((log) => terms[log.indicator] && terms[log.contributor]) && "missing-terms"].filter(Boolean);
|
|
1468
|
+
var operationOf = (eru, modelId) => eru.operation ? [
|
|
1469
|
+
{
|
|
1470
|
+
id: eru.operation["@id"],
|
|
1471
|
+
...getUnitsAndLabel(eru.operation),
|
|
1472
|
+
modelId,
|
|
1473
|
+
weightedValue: eru.value
|
|
1474
|
+
}
|
|
1475
|
+
] : [];
|
|
1476
|
+
var inputsOf = (impactAssessment, contributor, modelId) => Object.values(
|
|
1477
|
+
(impactAssessment.emissionsResourceUse || []).filter((eru) => {
|
|
1478
|
+
var _a;
|
|
1479
|
+
return eru.term["@id"] === contributor && ((_a = eru.inputs) == null ? void 0 : _a.length);
|
|
1480
|
+
}).reduce(
|
|
1481
|
+
(acc, eru) => {
|
|
1482
|
+
var _a;
|
|
1483
|
+
const inputId = eru.inputs.map((i) => i["@id"]).join(";");
|
|
1484
|
+
const operations = operationOf(eru, modelId);
|
|
1485
|
+
return {
|
|
1486
|
+
...acc,
|
|
1487
|
+
[inputId]: acc[inputId] ? {
|
|
1488
|
+
...acc[inputId],
|
|
1489
|
+
weightedValue: acc[inputId].weightedValue + eru.value,
|
|
1490
|
+
modelId,
|
|
1491
|
+
operations: [...acc[inputId].operations, ...operations]
|
|
1492
|
+
} : {
|
|
1493
|
+
id: inputId,
|
|
1494
|
+
...!((_a = eru.inputs) == null ? void 0 : _a.length) ? { label: "Input group (under development)" } : getUnitsAndLabel(eru.inputs),
|
|
1495
|
+
modelId,
|
|
1496
|
+
weightedValue: eru.value,
|
|
1497
|
+
operations
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
},
|
|
1501
|
+
{}
|
|
1502
|
+
)
|
|
1503
|
+
);
|
|
1504
|
+
var parseLogValues = (impactAssessment, values) => {
|
|
1505
|
+
const log = values[0];
|
|
1506
|
+
const weightedValue = values.reduce((total, curr) => total + curr.weightedValue, 0);
|
|
1507
|
+
const value = values.reduce((total, curr) => total + curr.value, 0);
|
|
1508
|
+
return {
|
|
1509
|
+
...log,
|
|
1510
|
+
weightedValue,
|
|
1511
|
+
value,
|
|
1512
|
+
inputs: inputsOf(impactAssessment, log.contributor, log.modelId)
|
|
1513
|
+
};
|
|
1514
|
+
};
|
|
1515
|
+
var processContributions = (record, nodes) => Object.entries(record || {}).flatMap(([index, byIndicator]) => {
|
|
1516
|
+
const indicator = nodes[+index];
|
|
1517
|
+
return indicator ? Object.entries(byIndicator).flatMap(
|
|
1518
|
+
([impactTermId, modelContr]) => Object.entries(modelContr).map(([modelId, weightedValue]) => {
|
|
1519
|
+
const contributor = indicator.term["@id"];
|
|
1520
|
+
const value = indicator.value || 0;
|
|
1521
|
+
const coefficient = value !== 0 ? weightedValue / value : 0;
|
|
1522
|
+
return coefficient > 0 ? { indicator: impactTermId, contributor, coefficient, modelId, weightedValue, value } : null;
|
|
1523
|
+
})
|
|
1524
|
+
) : null;
|
|
1525
|
+
});
|
|
1526
|
+
var hierarchyContributions = (impactAssessment, stored) => {
|
|
1527
|
+
const logs = [
|
|
1528
|
+
...processContributions(stored == null ? void 0 : stored.emissionsResourceUse, impactAssessment.emissionsResourceUse || []),
|
|
1529
|
+
...processContributions(stored == null ? void 0 : stored.impacts, impactAssessment.impacts || [])
|
|
1530
|
+
].filter(Boolean);
|
|
1531
|
+
const grouped = logs.reduce(
|
|
1532
|
+
(acc, log) => {
|
|
1533
|
+
const key = [log.indicator, log.contributor, log.modelId].join("/");
|
|
1534
|
+
return { ...acc, [key]: [...acc[key] || [], log] };
|
|
1535
|
+
},
|
|
1536
|
+
{}
|
|
1537
|
+
);
|
|
1538
|
+
return Object.values(grouped).map((values) => parseLogValues(impactAssessment, values));
|
|
1539
|
+
};
|
|
1540
|
+
var missingTermIds = (logs, terms = {}) => utils.unique(logs.flatMap((l) => {
|
|
1541
|
+
var _a;
|
|
1542
|
+
return [l.contributor, l.indicator, ...((_a = l.inputs) != null ? _a : []).map((i) => i.id)];
|
|
1543
|
+
}).filter((id) => !terms[id]));
|
|
1544
|
+
var hierarchyModels = (logs, terms) => [
|
|
1545
|
+
...logs.some((log) => log.modelId === defaultModelId(log.indicator)) ? [{ "@id": defaultIndicatorsModelId, name: defaultIndicatorsModelName }] : [],
|
|
1546
|
+
...utils.unique(logs.map(({ modelId }) => terms[modelId] || { "@id": modelId }))
|
|
1547
|
+
];
|
|
1548
|
+
var forModel = (log, modelId) => log.modelId === (modelId === defaultIndicatorsModelId ? defaultModelId(log.indicator) : modelId);
|
|
1549
|
+
var withCategory = (log, terms, category) => {
|
|
1550
|
+
var _a;
|
|
1551
|
+
const included = category && [schema.TermTermType.emission, schema.TermTermType.resourceUse].includes((_a = terms[log.contributor]) == null ? void 0 : _a.termType);
|
|
1552
|
+
const label = category ? categoryLabel(category) : void 0;
|
|
1553
|
+
return included ? {
|
|
1554
|
+
...log,
|
|
1555
|
+
category: emissionCategoryValue(log.contributor, category) || `Not available for ${label}`,
|
|
1556
|
+
categoryLabel: label
|
|
1557
|
+
} : log;
|
|
1558
|
+
};
|
|
1559
|
+
var hierarchyData = (logs, terms, modelId, category) => logs.filter((log) => forModel(log, modelId)).map((log) => withCategory(log, terms, category));
|
|
678
1560
|
|
|
679
|
-
// src/charts/
|
|
1561
|
+
// src/charts/hierarchy/data.ts
|
|
1562
|
+
var nonLCAIndicatorsId = "nonLCAIndicators";
|
|
1563
|
+
var childLabel = (indicator, child) => {
|
|
1564
|
+
const prefixRemoved = indicator.type === "endpoint" /* endpoint */ && child.label.split(", ")[1];
|
|
1565
|
+
return prefixRemoved ? "[...]" + prefixRemoved.charAt(0).toUpperCase() + prefixRemoved.slice(1) : child.label;
|
|
1566
|
+
};
|
|
1567
|
+
var linkIndicators = (indicators) => {
|
|
1568
|
+
const all = Object.values(indicators);
|
|
1569
|
+
return all.map((indicator) => ({
|
|
1570
|
+
...indicator,
|
|
1571
|
+
children: indicator.children.map(
|
|
1572
|
+
(child) => {
|
|
1573
|
+
var _a, _b;
|
|
1574
|
+
return child.type === "midpoint" /* midpoint */ ? {
|
|
1575
|
+
...child,
|
|
1576
|
+
label: childLabel(indicator, child),
|
|
1577
|
+
modelId: ((_a = indicators[child.id]) == null ? void 0 : _a.modelId) || child.modelId,
|
|
1578
|
+
children: (_b = indicators[child.id]) == null ? void 0 : _b.children
|
|
1579
|
+
} : child;
|
|
1580
|
+
}
|
|
1581
|
+
)
|
|
1582
|
+
})).filter((indicator) => !all.some((parent) => parent.children.some((child) => child.id === indicator.id)));
|
|
1583
|
+
};
|
|
1584
|
+
var otherIndicators = (children) => children.length ? {
|
|
1585
|
+
id: nonLCAIndicatorsId,
|
|
1586
|
+
label: "Other indicators",
|
|
1587
|
+
term: "n/a",
|
|
1588
|
+
modelId: "n/a",
|
|
1589
|
+
units: "",
|
|
1590
|
+
value: null,
|
|
1591
|
+
weightedValue: null,
|
|
1592
|
+
type: "endpoint" /* endpoint */,
|
|
1593
|
+
fraction: null,
|
|
1594
|
+
children
|
|
1595
|
+
} : null;
|
|
1596
|
+
var hierarchyChildren = (data2, terms) => {
|
|
1597
|
+
const linked = linkIndicators(groupIndicators(data2, terms, sumIndicators(data2)));
|
|
1598
|
+
const endpoints = linked.filter((indicator) => indicator.type === "endpoint" /* endpoint */);
|
|
1599
|
+
const midpoints = linked.filter((indicator) => indicator.type === "midpoint" /* midpoint */);
|
|
1600
|
+
return endpoints.length ? [...endpoints, otherIndicators(midpoints)].filter(Boolean) : linked;
|
|
1601
|
+
};
|
|
1602
|
+
var hierarchyLegend = (data2, includedTypes) => {
|
|
1603
|
+
var _a;
|
|
1604
|
+
const shown = legend.filter(({ type }) => includedTypes.includes(type));
|
|
1605
|
+
const categoryLabel2 = (_a = data2.find((d) => d.categoryLabel)) == null ? void 0 : _a.categoryLabel;
|
|
1606
|
+
return categoryLabel2 ? shown.map((l) => l.type === "category" /* category */ ? { ...l, text: categoryLabel2 } : l) : shown;
|
|
1607
|
+
};
|
|
1608
|
+
var extraInputKeys = ["input", "input value"];
|
|
1609
|
+
var extraOperationKeys = ["operation", "operation value"];
|
|
1610
|
+
var dataToCsv = (keys, terms) => (data2) => {
|
|
1611
|
+
var _a;
|
|
1612
|
+
return [
|
|
1613
|
+
// line for the data
|
|
1614
|
+
toLine([
|
|
1615
|
+
...keys.map((k) => {
|
|
1616
|
+
var _a2;
|
|
1617
|
+
return ((_a2 = terms[data2[k]]) == null ? void 0 : _a2.name) || data2[k] || "";
|
|
1618
|
+
}),
|
|
1619
|
+
...extraInputKeys.map(() => ""),
|
|
1620
|
+
...extraOperationKeys.map(() => "")
|
|
1621
|
+
]),
|
|
1622
|
+
// lines for each inputs and operations
|
|
1623
|
+
...((_a = data2.inputs) != null ? _a : []).flatMap((input) => {
|
|
1624
|
+
var _a2;
|
|
1625
|
+
return [
|
|
1626
|
+
toLine([...keys.map(() => ""), input.label, input.weightedValue, ...extraOperationKeys.map(() => "")]),
|
|
1627
|
+
...((_a2 = input.operations) != null ? _a2 : []).map(
|
|
1628
|
+
(operation) => toLine([...keys.map(() => ""), ...extraInputKeys.map(() => ""), operation.label, operation.weightedValue])
|
|
1629
|
+
)
|
|
1630
|
+
];
|
|
1631
|
+
})
|
|
1632
|
+
];
|
|
1633
|
+
};
|
|
1634
|
+
var hierarchyCsv = (data2, terms) => {
|
|
1635
|
+
const keys = Object.keys(data2[0]).filter((k) => k !== "inputs");
|
|
1636
|
+
return [toLine([...keys, ...extraInputKeys, ...extraOperationKeys]), ...data2.flatMap(dataToCsv(keys, terms))].join(
|
|
1637
|
+
"\n"
|
|
1638
|
+
);
|
|
1639
|
+
};
|
|
1640
|
+
var width = 800;
|
|
1641
|
+
var margin = { top: 10, right: 20, bottom: 10, left: 20 };
|
|
1642
|
+
var fontSize = Math.floor(width / 100);
|
|
1643
|
+
var nodePadding = fontSize / 2;
|
|
1644
|
+
var lineHeight = fontSize * 1.2;
|
|
1645
|
+
var barHeight = lineHeight;
|
|
1646
|
+
var barRadius = 4;
|
|
1647
|
+
var nodeHeight = nodePadding * 2 + 2 * lineHeight + barHeight;
|
|
1648
|
+
var nodeWidth = nodeHeight * 2 * 1.618;
|
|
1649
|
+
var nodeContentWidth = nodeWidth - nodePadding * 2;
|
|
1650
|
+
var layerSpacing = (width - margin.left - margin.right - nodeWidth * 5) / 4;
|
|
1651
|
+
var nodeMargin = nodePadding * 0.75;
|
|
1652
|
+
var nodeSpacingFactor = (nodeHeight + nodeMargin * 2) / nodeHeight;
|
|
1653
|
+
var nodeDuration = 600;
|
|
1654
|
+
var linkDuration = 200;
|
|
1655
|
+
var groupDelay = nodeDuration - 100;
|
|
1656
|
+
var greyedOpacity = 0.2;
|
|
1657
|
+
var groupOverlap = lineHeight - nodePadding - nodeMargin;
|
|
1658
|
+
var groupNodeSpace = nodeHeight - 2 * groupOverlap;
|
|
1659
|
+
var collapseChildren = (node) => {
|
|
1660
|
+
node._children = node.children || node._children;
|
|
1661
|
+
node.children = null;
|
|
1662
|
+
return node;
|
|
1663
|
+
};
|
|
1664
|
+
var restoreChildren = (node) => {
|
|
1665
|
+
node.children = node._children;
|
|
1666
|
+
node._children = null;
|
|
1667
|
+
return node;
|
|
1668
|
+
};
|
|
1669
|
+
var childrenOf = (node) => (node == null ? void 0 : node.children) || (node == null ? void 0 : node._children) || [];
|
|
1670
|
+
var enterStagger = (parent, child) => Math.max(childrenOf(parent).indexOf(child), 0) * 10;
|
|
1671
|
+
var exitStagger = (parent, child) => {
|
|
1672
|
+
const children = childrenOf(parent);
|
|
1673
|
+
const index = children.indexOf(child);
|
|
1674
|
+
return index < 0 ? 0 : (children.length - index - 1) * 10;
|
|
1675
|
+
};
|
|
1676
|
+
var wrap = (selection, maxWidth = nodeContentWidth) => {
|
|
1677
|
+
selection.each(function(d) {
|
|
1678
|
+
const t = d3Selection.select(this);
|
|
1679
|
+
const units = utils.ellipsis(d.data.units, 20);
|
|
1680
|
+
d3wrap(t, {
|
|
1681
|
+
maxWidth,
|
|
1682
|
+
maxLines: d.data.showBar ? 2 : 3,
|
|
1683
|
+
vcentre: !d.data.showBar,
|
|
1684
|
+
words: [...d.data.label.split(/\s+/), units],
|
|
1685
|
+
ownLineWord: units,
|
|
1686
|
+
nHeight: nodeHeight,
|
|
1687
|
+
lHeight: lineHeight
|
|
1688
|
+
});
|
|
1689
|
+
});
|
|
1690
|
+
};
|
|
1691
|
+
var setNodeSize = (selection) => selection.attr("width", nodeWidth).attr("height", nodeHeight);
|
|
1692
|
+
var formatBar = (selection) => selection.attr("height", barHeight).attr("rx", `${barRadius}`).attr("ry", `${barRadius}`).attr("y", nodeHeight - nodePadding - barHeight).attr("x", nodePadding);
|
|
1693
|
+
var formatBarPath = (data2) => {
|
|
1694
|
+
const x = nodePadding;
|
|
1695
|
+
const y = nodeHeight - nodePadding - barHeight;
|
|
1696
|
+
const h = barHeight;
|
|
1697
|
+
const w = fractionWidth(data2);
|
|
1698
|
+
const isFull = data2.fraction >= 0.99;
|
|
1699
|
+
const safeW = data2.fraction < 0.01 ? 0 : Math.max(w, barRadius);
|
|
1700
|
+
const safeH = Math.max(h, 2 * barRadius);
|
|
1701
|
+
return isFull ? `
|
|
1702
|
+
M ${x + safeW - barRadius}, ${y}
|
|
1703
|
+
a ${barRadius},${barRadius} 0 0 1 ${barRadius},${barRadius} v ${safeH - 2 * barRadius}
|
|
1704
|
+
a ${barRadius},${barRadius} 0 0 1 -${barRadius},${barRadius} h -${safeW - 2 * barRadius}
|
|
1705
|
+
a ${barRadius},${barRadius} 0 0 1 -${barRadius},-${barRadius} v -${safeH - 2 * barRadius}
|
|
1706
|
+
a ${barRadius},${barRadius} 0 0 1 ${barRadius},-${barRadius}
|
|
1707
|
+
z
|
|
1708
|
+
` : `
|
|
1709
|
+
M ${x + safeW}, ${y}
|
|
1710
|
+
v ${safeH}
|
|
1711
|
+
h -${safeW - barRadius}
|
|
1712
|
+
a ${barRadius},${barRadius} 0 0 1 -${barRadius},-${barRadius}
|
|
1713
|
+
v -${safeH - 2 * barRadius}
|
|
1714
|
+
a ${barRadius},${barRadius} 0 0 1 ${barRadius},-${barRadius}
|
|
1715
|
+
z
|
|
1716
|
+
`;
|
|
1717
|
+
};
|
|
1718
|
+
var groupButtonWidth = nodeWidth * 2 / 7;
|
|
1719
|
+
var groupButtonHeight = nodePadding * 2 + lineHeight + 3;
|
|
1720
|
+
var setGroupButtonXY = (selection) => selection.attr("width", groupButtonWidth).attr("height", (d) => d._groupOpen ? lineHeight + nodePadding * 2 : groupButtonHeight).attr("x", (d) => d._groupOpen ? nodeWidth * 2.5 / 7 : nodeWidth / 2 - groupButtonWidth / 2).attr(
|
|
1721
|
+
"y",
|
|
1722
|
+
(d) => d._groupOpen ? groupOverlap + (groupNodeSpace - (lineHeight + nodePadding * 2)) / 2 : nodePadding + lineHeight + 6
|
|
1723
|
+
);
|
|
1724
|
+
var formatNumber = (value) => utils.isNumber(value) ? utils.toPrecision(value, 3) : "N/A";
|
|
1725
|
+
var isTypeWithoutValue = (nodeDatum) => ["input" /* input */, "operation" /* operation */].includes(nodeDatum.data.type);
|
|
1726
|
+
var getWeightedValueText = (nodeDatum) => {
|
|
1727
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1728
|
+
return `${formatNumber(nodeDatum.data.weightedValue)}${((_a = nodeDatum.parent) == null ? void 0 : _a.data.value) === null ? "" : ` of ${formatNumber(((_b = nodeDatum.parent) == null ? void 0 : _b.data.value) || ((_c = nodeDatum.parent) == null ? void 0 : _c.data.weightedValue) || 0)}`} ${utils.isNumber((_d = nodeDatum.parent) == null ? void 0 : _d.data.value) ? (_f = (_e = nodeDatum.parent) == null ? void 0 : _e.data) == null ? void 0 : _f.units : (_i = (_h = (_g = nodeDatum.parent) == null ? void 0 : _g.parent) == null ? void 0 : _h.data) == null ? void 0 : _i.units}`;
|
|
1729
|
+
};
|
|
1730
|
+
var generateTipData = (nodeDatum, { baseUrl, schemaBaseUrl }) => {
|
|
1731
|
+
var _a;
|
|
1732
|
+
return {
|
|
1733
|
+
hasWeightedValue: nodeDatum.data.weightedValue,
|
|
1734
|
+
weightedValueText: getWeightedValueText(nodeDatum),
|
|
1735
|
+
valueKeyHref: `${schemaBaseUrl()}/${schema.SchemaType.Indicator}#value`,
|
|
1736
|
+
modelDocsHref: nodeDatum.data.modelDocs,
|
|
1737
|
+
valueValueText: utils.isNumber(nodeDatum.data.value) ? `${formatNumber(nodeDatum.data.value)} ${nodeDatum.data.units}` : isTypeWithoutValue(nodeDatum) ? "" : "no contribution",
|
|
1738
|
+
hasValue: nodeDatum.data.value,
|
|
1739
|
+
modelKeyHref: `${schemaBaseUrl()}/${schema.SchemaType.Indicator}#methodModel`,
|
|
1740
|
+
modelValue: nodeDatum.data.modelName,
|
|
1741
|
+
modelValueHref: `${baseUrl()}/term/${nodeDatum.data.modelId}`,
|
|
1742
|
+
termKeyHref: `${schemaBaseUrl()}/${schema.SchemaType.Indicator}#term`,
|
|
1743
|
+
terms: (_a = nodeDatum.data.terms) == null ? void 0 : _a.map((term2) => ({
|
|
1744
|
+
text: term2.name || term2["@id"],
|
|
1745
|
+
href: `${baseUrl()}/term/${term2["@id"]}`
|
|
1746
|
+
}))
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
var addTooltip = (selection, { tooltip, urls }) => {
|
|
1750
|
+
let hoveringTip = false;
|
|
1751
|
+
let hoveringNode = false;
|
|
1752
|
+
let lastNodeId;
|
|
1753
|
+
const closeTip = () => {
|
|
1754
|
+
tooltip.close();
|
|
1755
|
+
d3Selection.selectAll(".tip-target").classed("tip-target", false);
|
|
1756
|
+
lastNodeId = null;
|
|
1757
|
+
};
|
|
1758
|
+
const startCloseTip = () => {
|
|
1759
|
+
setTimeout(() => {
|
|
1760
|
+
if (!hoveringTip && !hoveringNode) {
|
|
1761
|
+
closeTip();
|
|
1762
|
+
}
|
|
1763
|
+
}, 100);
|
|
1764
|
+
};
|
|
1765
|
+
selection.on("click", closeTip).on("pointerenter", (event, nodeDatum) => {
|
|
1766
|
+
hoveringNode = true;
|
|
1767
|
+
if (lastNodeId === nodeDatum.data.id) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
closeTip();
|
|
1771
|
+
lastNodeId = nodeDatum.data.id;
|
|
1772
|
+
d3Selection.select(event.target).classed("tip-target", true);
|
|
1773
|
+
tooltip.open(generateTipData(nodeDatum, urls), event.target);
|
|
1774
|
+
d3Selection.select(tooltip.element()).on("pointerenter", () => {
|
|
1775
|
+
hoveringTip = true;
|
|
1776
|
+
}).on("pointerleave", () => {
|
|
1777
|
+
hoveringTip = false;
|
|
1778
|
+
startCloseTip();
|
|
1779
|
+
});
|
|
1780
|
+
}).on("pointerleave", () => {
|
|
1781
|
+
hoveringNode = false;
|
|
1782
|
+
startCloseTip();
|
|
1783
|
+
});
|
|
1784
|
+
return selection;
|
|
1785
|
+
};
|
|
1786
|
+
var mergedNodes = (selection, options) => {
|
|
1787
|
+
selection.selectAll(".node-box").classed("node-openable", (d) => !!d.height).classed("node-open", (d) => d._open).call(setNodeSize).attr("stroke", ({ data: data2 }) => nodeBorderColours[data2.type]).attr("fill", (d) => nodeColours[d.data.type]).attr("stroke-width", "2").attr("rx", "3px").attr("ry", "3px").style("user-select", typeof window !== "undefined" && window.innerWidth < 768 ? "none" : null);
|
|
1788
|
+
selection.filter((d) => !d.data.group && d.data.id !== nonLCAIndicatorsId).call(addTooltip, options);
|
|
1789
|
+
selection.selectAll(".node-label").attr("y", (d) => d.data.showBar ? lineHeight / 2 + nodePadding / 2 : 0).attr("x", nodeWidth / 2).attr("font-weight", "bold").attr("fill", "#4a4a4a").attr("text-anchor", "middle").attr("alignment-baseline", "central");
|
|
1790
|
+
const groups = selection.filter((d) => d.data.group);
|
|
1791
|
+
groups.selectAll(".group-border").attr("width", nodeWidth + nodePadding * 2).attr("x", -nodePadding).attr("y", nodeHeight - (lineHeight + nodePadding * 2)).attr("opacity", (d) => d._greyed && d._groupOpen ? greyedOpacity : d._groupOpen ? 1 : 0).attr(
|
|
1792
|
+
"height",
|
|
1793
|
+
(d) => d._groupOpen ? Math.abs(d.parent._groupChildren.length - d.parent.children.length) * (nodeHeight * nodeSpacingFactor) + nodePadding + (lineHeight + nodePadding * 2) / 2 : 0
|
|
1794
|
+
).attr("fill", "none").attr("stroke-dasharray", "2, 5").attr("stroke", "#b5b5b5").attr("stroke-width", "1");
|
|
1795
|
+
groups.selectAll(".group-label").attr("width", nodeWidth).attr("y", nodeHeight / 3).attr("x", nodeWidth / 2).text((d) => d.data.label).attr("opacity", (d) => d._groupOpen ? 0 : d._greyed ? greyedOpacity : 1).attr("fill", "#4a4a4a").attr("text-anchor", "middle").attr("alignment-baseline", "central").attr("font-weight", "bold");
|
|
1796
|
+
groups.selectAll(".group-button-border").attr("width", nodeWidth).attr("x", 0).attr("y", 0).attr("height", (d) => d._groupOpen ? "0" : "45").attr("fill", "none").attr("stroke-dasharray", "2, 5").attr("stroke", "#b5b5b5").attr("stroke-width", "1");
|
|
1797
|
+
groups.selectAll(".group-button").call(setGroupButtonXY).style("pointer-events", (d) => d._greyed ? "none" : "auto").style("stroke-opacity", (d) => d._greyed ? greyedOpacity : 1).style("display", "none").attr("fill", (d) => nodeColours[d.data.type]).attr("rx", "3px").attr("ry", "3px");
|
|
1798
|
+
groups.selectAll(".group-button-text").text((d) => d._groupOpen ? "Hide" : "Show").style("display", "none").attr("x", (d) => d._groupOpen ? nodeWidth / 2 : nodeWidth / 2).attr("y", (d) => d._groupOpen ? nodeMargin + lineHeight + lineHeight / 2 : nodeHeight / 3 + groupButtonHeight - 4).attr("font-size", () => `${fontSize * 1.3}px`).attr("fill", "#4a4a4a").attr("text-anchor", "middle").attr("alignment-baseline", "central");
|
|
1799
|
+
selection.selectAll(".sibling-mask").remove();
|
|
1800
|
+
selection.filter((d) => d._greyed && !d.data.group).append("rect").classed("node-openable", (d) => !!d.height).classed("sibling-mask mask-openable", true).attr("fill", "rgba(250, 250, 250)").attr("stroke", "rgba(250, 250, 250)").attr("fill-opacity", "0.8").attr("stroke-opacity", "0.9").attr("stroke-width", "2").attr("rx", "3px").attr("ry", "3px").call(setNodeSize);
|
|
1801
|
+
selection.filter((d) => d._greyed && d.data.group).append("rect").classed("sibling-mask", true).attr("fill", "rgba(250, 250, 250)").attr("stroke", "rgba(250, 250, 250)").attr("fill-opacity", "0.8").attr("stroke-opacity", "0.9").attr("stroke-width", "2").attr("rx", "3px").attr("ry", "3px").call(setGroupButtonXY);
|
|
1802
|
+
return selection;
|
|
1803
|
+
};
|
|
1804
|
+
var fractionCutoff = 0.7;
|
|
1805
|
+
var fractionWidth = (data2) => Math.min(Math.abs(data2.fraction), 1) * nodeContentWidth;
|
|
1806
|
+
var enterNodes = (selection) => {
|
|
1807
|
+
const nodes = selection.filter((d) => d.data.type !== "root" /* root */ && !d.data.group);
|
|
1808
|
+
nodes.append("rect").classed("node-box", true);
|
|
1809
|
+
nodes.append("text").classed("node-label", true).text(({ data: data2 }) => data2.label + " " + data2.units).attr("opacity", 0);
|
|
1810
|
+
setTimeout(() => {
|
|
1811
|
+
const labels = nodes.selectAll(".node-label").call(wrap);
|
|
1812
|
+
labels.attr("opacity", 1);
|
|
1813
|
+
});
|
|
1814
|
+
const groups = selection.filter((d) => d.data.group);
|
|
1815
|
+
groups.append("rect").attr("class", "group-border");
|
|
1816
|
+
groups.append("text").attr("class", "group-label");
|
|
1817
|
+
groups.append("rect").attr("class", "group-button-border");
|
|
1818
|
+
groups.append("rect").attr("class", "group-button");
|
|
1819
|
+
groups.append("text").attr("class", "group-button-text");
|
|
1820
|
+
const withBar = nodes.filter((d) => d.data.showBar);
|
|
1821
|
+
withBar.filter((d) => d.data.fraction > 0).append("rect").classed("bar-background", true).attr("width", nodeContentWidth).attr("fill", "white").call(formatBar);
|
|
1822
|
+
withBar.append("path").classed("bar-foreground", true).attr("d", (d) => formatBarPath(d.data)).attr("fill", (d) => nodeBorderColours[d.data.type]);
|
|
1823
|
+
withBar.append("text").classed("bar-label", true).text(
|
|
1824
|
+
({ data: data2 }) => data2.fraction === 0 ? "no contribution" : `${Math.round(100 * data2.fraction)}% ${Math.round(100 * data2.fraction) === 0 ? " (rounded)" : ""}`
|
|
1825
|
+
).attr("text-anchor", (d) => d.data.fraction === 0 ? "middle" : d.data.fraction > fractionCutoff ? "end" : "start").attr(
|
|
1826
|
+
"x",
|
|
1827
|
+
(d) => d.data.fraction === 0 ? nodeWidth / 2 : fractionWidth(d.data) + nodePadding + (d.data.fraction > fractionCutoff ? -4 : 4)
|
|
1828
|
+
).attr("y", nodeHeight - nodePadding - barHeight / 2).attr("fill", "#4a4a4a").attr("alignment-baseline", "central");
|
|
1829
|
+
return selection;
|
|
1830
|
+
};
|
|
1831
|
+
var mergedLinks = (selection, { togglingGroup, switchingSelection }) => selection.style("stroke", (d) => d.target._open && nodeBorderColours[d.source.data.type] || null).attr("stroke-dasharray", (d) => d.target.data.group ? "2,5" : null).attr("stroke-width", (d) => d.target._open ? 2 : d.target._groupOpen ? 0 : 1).attr("stroke-opacity", (d) => d.target._greyed ? 0.1 : 0.6).transition().duration(linkDuration).delay((d) => {
|
|
1832
|
+
var _a;
|
|
1833
|
+
const groupClosing = togglingGroup && !((_a = childrenOf(d.target.parent).find((n) => n.data.group)) == null ? void 0 : _a._groupOpen);
|
|
1834
|
+
return groupClosing ? groupDelay : (switchingSelection ? nodeDuration : 0) + enterStagger(d.source, d.target);
|
|
1835
|
+
}).attr("opacity", 1).attr("d", (d) => {
|
|
1836
|
+
const targetDy = d.target._groupOpen ? nodePadding + lineHeight / 2 + nodeMargin * 2 : nodeHeight / 2;
|
|
1837
|
+
return d3Shape.linkHorizontal()({
|
|
1838
|
+
source: [d.source.y + nodeWidth, d.source.x + nodeHeight / 2],
|
|
1839
|
+
target: [d.target.y, d.target.x + targetDy]
|
|
1840
|
+
});
|
|
1841
|
+
});
|
|
1842
|
+
var groupByPercentage = (node) => ["endpoint" /* endpoint */, "midpoint" /* midpoint */, "emission" /* emission */].includes(node.data.type);
|
|
1843
|
+
var groupPercentage = (node, percentage = 0.02) => {
|
|
1844
|
+
var _a, _b, _c;
|
|
1845
|
+
const grouped = (_a = node._children) == null ? void 0 : _a.filter((child) => child.data.fraction < percentage);
|
|
1846
|
+
if (((_b = node._children) == null ? void 0 : _b.length) > 6 && (grouped == null ? void 0 : grouped.length) > 1) {
|
|
1847
|
+
const type = node._children[0].data.type;
|
|
1848
|
+
const groupNode = {
|
|
1849
|
+
...d3Hierarchy.hierarchy({
|
|
1850
|
+
id: `${type}-group`,
|
|
1851
|
+
label: `${grouped.length} more under ${Math.round(percentage * 100)}%.`,
|
|
1852
|
+
weightedValue: null,
|
|
1853
|
+
value: null,
|
|
1854
|
+
type,
|
|
1855
|
+
group: true
|
|
1856
|
+
}),
|
|
1857
|
+
depth: node.depth + 1,
|
|
1858
|
+
height: 1,
|
|
1859
|
+
parent: node
|
|
1860
|
+
};
|
|
1861
|
+
const notGrouped = (_c = node._children) == null ? void 0 : _c.filter((child) => child.data.fraction >= percentage);
|
|
1862
|
+
node._children.splice(notGrouped.length, 0, groupNode);
|
|
1863
|
+
notGrouped.push(groupNode);
|
|
1864
|
+
node._groupChildren = node._children;
|
|
1865
|
+
node._children = notGrouped;
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
var showBar = (node) => {
|
|
1869
|
+
var _a, _b;
|
|
1870
|
+
return [
|
|
1871
|
+
((_a = node.parent) == null ? void 0 : _a.data.type) !== "root" /* root */,
|
|
1872
|
+
((_b = node.parent) == null ? void 0 : _b.data.id) !== nonLCAIndicatorsId,
|
|
1873
|
+
!node.data.group,
|
|
1874
|
+
!isNaN(node.data.fraction)
|
|
1875
|
+
].every(Boolean);
|
|
1876
|
+
};
|
|
1877
|
+
var totalWidthFor = (numLayers) => {
|
|
1878
|
+
const chartWidth = numLayers * nodeWidth + (numLayers - 1) * layerSpacing;
|
|
1879
|
+
return { chartWidth, totalWidth: Math.max(width, chartWidth + margin.left + margin.right) };
|
|
1880
|
+
};
|
|
1881
|
+
var hierarchyOf = (children) => d3Hierarchy.hierarchy({
|
|
1882
|
+
id: "dummy-root",
|
|
1883
|
+
label: "",
|
|
1884
|
+
terms: [],
|
|
1885
|
+
modelId: "",
|
|
1886
|
+
value: null,
|
|
1887
|
+
weightedValue: null,
|
|
1888
|
+
units: "",
|
|
1889
|
+
type: "root" /* root */,
|
|
1890
|
+
children
|
|
1891
|
+
});
|
|
1892
|
+
var prepareRoot = (root, terms) => root.sort((a, b) => (b.data.fraction || -Infinity) - (a.data.fraction || -Infinity)).eachAfter((node) => {
|
|
1893
|
+
var _a, _b;
|
|
1894
|
+
if (node.data.fraction === 0) {
|
|
1895
|
+
node.children = null;
|
|
1896
|
+
node.height = 0;
|
|
1897
|
+
}
|
|
1898
|
+
node.data.type !== "root" /* root */ && collapseChildren(node);
|
|
1899
|
+
groupByPercentage(node) && groupPercentage(node);
|
|
1900
|
+
node.data.showBar = showBar(node);
|
|
1901
|
+
node.data.modelName = (_a = terms == null ? void 0 : terms[node.data.modelId]) == null ? void 0 : _a.name;
|
|
1902
|
+
node.data.modelDocs = (_b = terms == null ? void 0 : terms[node.data.modelId]) == null ? void 0 : _b.docs;
|
|
1903
|
+
});
|
|
1904
|
+
var drawHierarchyChart = (svgElement, { data: data2, terms, tooltip, urls }) => {
|
|
1905
|
+
const tree = hierarchyOf(hierarchyChildren(data2, terms));
|
|
1906
|
+
const includedTypes = utils.unique(tree.descendants().map((n) => n.data.type));
|
|
1907
|
+
const root = prepareRoot(tree, terms);
|
|
1908
|
+
const numLayers = hierarchyLegend(data2, includedTypes).length;
|
|
1909
|
+
const { chartWidth, totalWidth } = totalWidthFor(numLayers);
|
|
1910
|
+
const svg = d3Selection.select(svgElement);
|
|
1911
|
+
svg.selectAll("*").remove();
|
|
1912
|
+
const innerHeight = (root.children.length - 1) * nodeHeight * nodeSpacingFactor + nodeHeight;
|
|
1913
|
+
const yMin = (nodeHeight - innerHeight) / 2 - margin.top;
|
|
1914
|
+
const yTotal = innerHeight + margin.top + margin.bottom;
|
|
1915
|
+
svg.attr("viewBox", [-20, yMin, totalWidth, yTotal].join(" ")).attr("font-size", `${fontSize}px`);
|
|
1916
|
+
let node = svg.append("g").selectAll("g");
|
|
1917
|
+
let link = svg.append("g").selectAll("path");
|
|
1918
|
+
const updateChart = ({ togglingGroup = false, switchingSelection = false } = {}) => {
|
|
1919
|
+
const treeLayout = d3Hierarchy.tree().nodeSize([nodeHeight, nodeWidth]).separation(
|
|
1920
|
+
(...neighbours) => neighbours.some((n) => n.data.group && n._groupOpen) ? 1 - groupOverlap / nodeHeight : nodeSpacingFactor
|
|
1921
|
+
);
|
|
1922
|
+
const tree2 = treeLayout(root);
|
|
1923
|
+
const links = tree2.links().filter((d) => d.source.data.type !== "root" /* root */) || [];
|
|
1924
|
+
const nodes = tree2.descendants().reverse();
|
|
1925
|
+
const offset = Math.max(0, (totalWidth - margin.left - margin.right - chartWidth) / 2);
|
|
1926
|
+
nodes.forEach((d) => {
|
|
1927
|
+
d.y = offset + (d.depth - 1) * (nodeWidth + layerSpacing);
|
|
1928
|
+
});
|
|
1929
|
+
const minimiseHeight = ({ previousTanslateY, previousYTotal }, depth) => {
|
|
1930
|
+
var _a, _b, _c;
|
|
1931
|
+
const maxYTotal = Math.max(previousYTotal, yTotal);
|
|
1932
|
+
const parent = tree2.find((n) => n.depth === depth - 1 && n._open);
|
|
1933
|
+
(_a = parent == null ? void 0 : parent.children) == null ? void 0 : _a.forEach((n) => n.x += previousTanslateY);
|
|
1934
|
+
const { min: minX, max: maxX } = ((parent == null ? void 0 : parent.children) || []).reduce(
|
|
1935
|
+
({ min, max }, n) => ({
|
|
1936
|
+
min: n.x < min ? n.x : min,
|
|
1937
|
+
max: n.x > max ? n.x : max
|
|
1938
|
+
}),
|
|
1939
|
+
{ min: parent == null ? void 0 : parent.x, max: parent == null ? void 0 : parent.x }
|
|
1940
|
+
);
|
|
1941
|
+
const { yMin: childrenYMin, yTotal: childrenYTotal } = ((_b = parent == null ? void 0 : parent.children) == null ? void 0 : _b.length) ? { yMin: minX - margin.top, yTotal: maxX - minX + margin.bottom + margin.top + nodeHeight } : { yMin: null, yTotal: null };
|
|
1942
|
+
const overflowAbove = yMin - childrenYMin;
|
|
1943
|
+
const overflowBelow = childrenYTotal + childrenYMin - (maxYTotal + yMin);
|
|
1944
|
+
const translateY = overflowAbove > 0 ? overflowAbove : overflowBelow > 0 ? Math.max(-overflowBelow, overflowAbove) : 0;
|
|
1945
|
+
(_c = parent == null ? void 0 : parent.children) == null ? void 0 : _c.forEach((n) => n.x += translateY);
|
|
1946
|
+
return {
|
|
1947
|
+
previousYTotal: Math.max(childrenYTotal, previousYTotal),
|
|
1948
|
+
previousTanslateY: translateY + previousTanslateY
|
|
1949
|
+
};
|
|
1950
|
+
};
|
|
1951
|
+
const depths = Array.from({ length: numLayers }, (_, i) => i + 2);
|
|
1952
|
+
const { previousYTotal: newHeight } = depths.reduce(minimiseHeight, {
|
|
1953
|
+
previousTanslateY: 0,
|
|
1954
|
+
previousYTotal: yTotal
|
|
1955
|
+
});
|
|
1956
|
+
svg.transition().duration(300).attr("viewBox", [-20, yMin, totalWidth, newHeight].join(" "));
|
|
1957
|
+
node = node.data(
|
|
1958
|
+
nodes.filter((d) => d.data.type !== "root" /* root */),
|
|
1959
|
+
(d) => {
|
|
1960
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1961
|
+
return `${(_c = (_b = (_a = d.parent) == null ? void 0 : _a.parent) == null ? void 0 : _b.parent) == null ? void 0 : _c.data.id}-${(_e = (_d = d.parent) == null ? void 0 : _d.parent) == null ? void 0 : _e.data.id}-${(_f = d.parent) == null ? void 0 : _f.data.id}-${d.data.id}`;
|
|
1962
|
+
}
|
|
1963
|
+
).join(
|
|
1964
|
+
(enter) => enter.append("g").call(enterNodes).attr("opacity", 0).attr("transform", (d) => "translate(" + (togglingGroup ? d.y : d.y - nodeWidth / 2) + "," + d.x + ")"),
|
|
1965
|
+
(update) => update,
|
|
1966
|
+
(exit) => {
|
|
1967
|
+
tooltip.close();
|
|
1968
|
+
return exit.style("pointer-events", "none").transition().duration(nodeDuration / 2).delay((d) => exitStagger(d.parent, d)).ease(d3Ease.easeElasticIn.amplitude(0.5).period(1)).attr("transform", (d) => "translate(" + (togglingGroup ? d.y : d.y - nodeWidth / 2) + "," + d.x + ")").attr("opacity", 0).remove().on("end", () => tooltip.close());
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
node.call(mergedNodes, { tooltip, urls }).transition().delay((d) => {
|
|
1972
|
+
var _a;
|
|
1973
|
+
const groupClosing = togglingGroup && !((_a = childrenOf(d.parent).find((n) => n.data.group)) == null ? void 0 : _a._groupOpen);
|
|
1974
|
+
return groupClosing ? groupDelay : (switchingSelection ? nodeDuration : 0) + enterStagger(d.parent, d);
|
|
1975
|
+
}).duration(nodeDuration).ease(d3Ease.easeElasticOut.amplitude(0.5).period(1)).attr("transform", (d) => `translate(${d.y},${d.x})`).attr("opacity", 1);
|
|
1976
|
+
node.selectAll(".node-openable").on("click", handleNodeClick);
|
|
1977
|
+
node.selectAll(".group-button").on("click", handleGroupButtonClick);
|
|
1978
|
+
link = link.data(
|
|
1979
|
+
links,
|
|
1980
|
+
(d) => {
|
|
1981
|
+
var _a, _b, _c;
|
|
1982
|
+
return `${(_b = (_a = d.source.parent) == null ? void 0 : _a.parent) == null ? void 0 : _b.data.id}-${(_c = d.source.parent) == null ? void 0 : _c.data.id}-${d.source.data.id}-${d.target.data.id}`;
|
|
1983
|
+
}
|
|
1984
|
+
).join(
|
|
1985
|
+
(enter) => enter.append("path").classed("link-path", true).attr("opacity", 0).attr("fill", "none").attr("stroke", "#b5b5b5"),
|
|
1986
|
+
(update) => update,
|
|
1987
|
+
(exit) => exit.transition().duration(linkDuration / 2).delay((d) => exitStagger(d.source, d.target)).attr("opacity", 0).remove()
|
|
1988
|
+
).call(mergedLinks, { togglingGroup, switchingSelection });
|
|
1989
|
+
};
|
|
1990
|
+
const handleGroupButtonClick = (_event, d) => {
|
|
1991
|
+
if (d._greyed) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
d._groupOpen = !d._groupOpen;
|
|
1995
|
+
const children = d.parent._groupChildren;
|
|
1996
|
+
d.parent._groupChildren = d.parent.children;
|
|
1997
|
+
d.parent.children = children;
|
|
1998
|
+
updateChart({ togglingGroup: true });
|
|
1999
|
+
};
|
|
2000
|
+
const handleNodeClick = (_event, d) => {
|
|
2001
|
+
d._open = !d._open;
|
|
2002
|
+
const switchingSelection = "_greyed" in d && d._greyed;
|
|
2003
|
+
childrenOf(d.parent).forEach((child) => {
|
|
2004
|
+
child._greyed = false;
|
|
2005
|
+
if (child !== d) {
|
|
2006
|
+
child._open = false;
|
|
2007
|
+
collapseChildren(child);
|
|
2008
|
+
child._greyed = d._open;
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
d.children ? collapseChildren(d) : restoreChildren(d);
|
|
2012
|
+
updateChart({ switchingSelection });
|
|
2013
|
+
};
|
|
2014
|
+
handleNodeClick(
|
|
2015
|
+
null,
|
|
2016
|
+
root.find((n) => n.depth === 1)
|
|
2017
|
+
);
|
|
2018
|
+
return { includedTypes };
|
|
2019
|
+
};
|
|
2020
|
+
var defaultHorizontalBarConfig = Object.freeze({
|
|
2021
|
+
options: {
|
|
2022
|
+
indexAxis: "y",
|
|
2023
|
+
scales: {
|
|
2024
|
+
x: {
|
|
2025
|
+
grid: {
|
|
2026
|
+
drawOnChartArea: false
|
|
2027
|
+
},
|
|
2028
|
+
ticks: {
|
|
2029
|
+
callback: (value) => utils.toPrecision(Number(value), 3)
|
|
2030
|
+
}
|
|
2031
|
+
},
|
|
2032
|
+
y: {
|
|
2033
|
+
grid: {
|
|
2034
|
+
drawOnChartArea: false
|
|
2035
|
+
},
|
|
2036
|
+
ticks: {
|
|
2037
|
+
crossAlign: "near"
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
var defaultHorizontalTooltip = ({ label, count, includedItems }) => (includedItems == null ? void 0 : includedItems.length) ? includedItems.map(defaultHorizontalTooltip).join("</br>") : `${label}: ${count}`;
|
|
2044
|
+
var rowHeight = 40;
|
|
2045
|
+
var padding = 60;
|
|
2046
|
+
var horizontalBarHeight = (minHeight, maxHeight, nbRows) => Math.min(Math.max(nbRows * rowHeight + padding, minHeight), maxHeight);
|
|
680
2047
|
var logsTotalValue = (logs, includeNegativeValues = true) => utils.sum((includeNegativeValues ? logs : logs.filter((curr) => curr.value >= 0)).map((v) => v.value));
|
|
681
2048
|
var valueRatio = (value, total) => total === 0 ? 0 : utils.toPrecision(value * 100 / Math.abs(total), 2);
|
|
682
2049
|
var chartLabel = (value, total) => {
|
|
@@ -701,7 +2068,7 @@ var logToCsv = (logs, impact) => [
|
|
|
701
2068
|
}
|
|
702
2069
|
)
|
|
703
2070
|
].join("\n");
|
|
704
|
-
var
|
|
2071
|
+
var processContributions2 = (record, nodes, impacts) => Object.entries(record || {}).flatMap(([index, impactContr]) => {
|
|
705
2072
|
const indicator = nodes[+index];
|
|
706
2073
|
return indicator ? Object.entries(impactContr).flatMap(([impactTermId, modelContr]) => {
|
|
707
2074
|
const impact = impacts == null ? void 0 : impacts.find((v) => v.term["@id"] === impactTermId);
|
|
@@ -721,12 +2088,12 @@ var processContributions = (record, nodes, impacts) => Object.entries(record ||
|
|
|
721
2088
|
}) : null;
|
|
722
2089
|
});
|
|
723
2090
|
var parseContributions = (impactAssessment, contributions) => [
|
|
724
|
-
...
|
|
2091
|
+
...processContributions2(
|
|
725
2092
|
contributions == null ? void 0 : contributions.emissionsResourceUse,
|
|
726
2093
|
impactAssessment.emissionsResourceUse || [],
|
|
727
2094
|
(impactAssessment == null ? void 0 : impactAssessment.impacts) || []
|
|
728
2095
|
),
|
|
729
|
-
...
|
|
2096
|
+
...processContributions2(contributions == null ? void 0 : contributions.impacts, impactAssessment.impacts || [], (impactAssessment == null ? void 0 : impactAssessment.endpoints) || [])
|
|
730
2097
|
].filter(Boolean);
|
|
731
2098
|
var groupLogsById = (logs) => Object.values(
|
|
732
2099
|
logs.reduce(
|
|
@@ -785,6 +2152,42 @@ var breakdownScale = (values) => {
|
|
|
785
2152
|
};
|
|
786
2153
|
};
|
|
787
2154
|
|
|
2155
|
+
// src/charts/line.ts
|
|
2156
|
+
var defaultLineSettings = Object.freeze({
|
|
2157
|
+
type: "line",
|
|
2158
|
+
options: {
|
|
2159
|
+
scales: {
|
|
2160
|
+
x: {
|
|
2161
|
+
type: "time",
|
|
2162
|
+
time: {
|
|
2163
|
+
unit: "day"
|
|
2164
|
+
},
|
|
2165
|
+
ticks: {
|
|
2166
|
+
font: defaultTicksFont
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
});
|
|
2172
|
+
var registerChart = (items = []) => () => {
|
|
2173
|
+
chart_js.Chart.register(
|
|
2174
|
+
chart_js.BarController,
|
|
2175
|
+
chart_js.LineController,
|
|
2176
|
+
chart_js.CategoryScale,
|
|
2177
|
+
chart_js.LinearScale,
|
|
2178
|
+
chart_js.PointElement,
|
|
2179
|
+
chart_js.BarElement,
|
|
2180
|
+
chart_js.LineElement,
|
|
2181
|
+
chart_js.Title,
|
|
2182
|
+
chart_js.Tooltip,
|
|
2183
|
+
chart_js.Legend,
|
|
2184
|
+
chart_js.TimeScale,
|
|
2185
|
+
annotationPlugin__default.default,
|
|
2186
|
+
...items
|
|
2187
|
+
);
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
exports.ChartNodeType = ChartNodeType;
|
|
788
2191
|
exports.ColorPalette = ColorPalette;
|
|
789
2192
|
exports.afterBarDrawPlugin = afterBarDrawPlugin;
|
|
790
2193
|
exports.axisHoverPlugin = axisHoverPlugin;
|
|
@@ -801,33 +2204,79 @@ exports.chartBreakdownLabel = chartBreakdownLabel;
|
|
|
801
2204
|
exports.chartLabel = chartLabel;
|
|
802
2205
|
exports.chartLabelMaxLength = chartLabelMaxLength;
|
|
803
2206
|
exports.colorToRgba = colorToRgba;
|
|
2207
|
+
exports.contributionBarHeight = contributionBarHeight;
|
|
2208
|
+
exports.contributionChartConfig = contributionChartConfig;
|
|
2209
|
+
exports.contributionChartHeight = contributionChartHeight;
|
|
2210
|
+
exports.contributionCsv = contributionCsv;
|
|
2211
|
+
exports.contributionData = contributionData;
|
|
2212
|
+
exports.contributionHoverHeight = contributionHoverHeight;
|
|
2213
|
+
exports.contributionItemColor = contributionItemColor;
|
|
2214
|
+
exports.convertToSvg = convertToSvg;
|
|
2215
|
+
exports.d3ellipse = d3ellipse;
|
|
2216
|
+
exports.d3wrap = d3wrap;
|
|
2217
|
+
exports.dataAtIndex = dataAtIndex;
|
|
2218
|
+
exports.datasetCenter = datasetCenter;
|
|
804
2219
|
exports.defaultBarDatasets = defaultBarDatasets;
|
|
805
2220
|
exports.defaultBarSettings = defaultBarSettings;
|
|
2221
|
+
exports.defaultChartSettings = defaultChartSettings;
|
|
2222
|
+
exports.defaultContributionSettings = defaultContributionSettings;
|
|
2223
|
+
exports.defaultDistributionSettings = defaultDistributionSettings;
|
|
806
2224
|
exports.defaultHorizontalBarConfig = defaultHorizontalBarConfig;
|
|
807
2225
|
exports.defaultHorizontalTooltip = defaultHorizontalTooltip;
|
|
2226
|
+
exports.defaultIndicatorsModelId = defaultIndicatorsModelId;
|
|
2227
|
+
exports.defaultIndicatorsModelName = defaultIndicatorsModelName;
|
|
2228
|
+
exports.defaultLineSettings = defaultLineSettings;
|
|
2229
|
+
exports.defaultModelId = defaultModelId;
|
|
808
2230
|
exports.defaultTicksFont = defaultTicksFont;
|
|
2231
|
+
exports.distributionChart = distributionChart;
|
|
2232
|
+
exports.downloadFile = downloadFile;
|
|
2233
|
+
exports.downloadPng = downloadPng;
|
|
2234
|
+
exports.downloadSvg = downloadSvg;
|
|
2235
|
+
exports.drawHierarchyChart = drawHierarchyChart;
|
|
809
2236
|
exports.emissionCategories = emissionCategories;
|
|
810
2237
|
exports.emissionCategoriesOf = emissionCategoriesOf;
|
|
811
2238
|
exports.emissionCategoryValue = emissionCategoryValue;
|
|
812
2239
|
exports.escape = escape;
|
|
2240
|
+
exports.exportAsSVG = exportAsSVG;
|
|
2241
|
+
exports.formatPrecision = formatPrecision;
|
|
813
2242
|
exports.getColor = getColor;
|
|
2243
|
+
exports.getUnitsAndLabel = getUnitsAndLabel;
|
|
814
2244
|
exports.groupDataByCategory = groupDataByCategory;
|
|
2245
|
+
exports.groupIndicators = groupIndicators;
|
|
815
2246
|
exports.groupLogsById = groupLogsById;
|
|
816
2247
|
exports.hasNegativeContributions = hasNegativeContributions;
|
|
2248
|
+
exports.hasNegativeValues = hasNegativeValues;
|
|
817
2249
|
exports.hexToRgba = hexToRgba;
|
|
2250
|
+
exports.hierarchyChildren = hierarchyChildren;
|
|
2251
|
+
exports.hierarchyContributions = hierarchyContributions;
|
|
2252
|
+
exports.hierarchyCsv = hierarchyCsv;
|
|
2253
|
+
exports.hierarchyData = hierarchyData;
|
|
2254
|
+
exports.hierarchyLegend = hierarchyLegend;
|
|
2255
|
+
exports.hierarchyModels = hierarchyModels;
|
|
2256
|
+
exports.hierarchyTermTypes = hierarchyTermTypes;
|
|
2257
|
+
exports.hierarchyWarnings = hierarchyWarnings;
|
|
818
2258
|
exports.horizontalBarHeight = horizontalBarHeight;
|
|
819
2259
|
exports.increaseScaleLimits = increaseScaleLimits;
|
|
820
2260
|
exports.indicatorLogs = indicatorLogs;
|
|
2261
|
+
exports.isParametric = isParametric;
|
|
2262
|
+
exports.legend = legend;
|
|
2263
|
+
exports.linkIndicators = linkIndicators;
|
|
821
2264
|
exports.listColor = listColor;
|
|
822
2265
|
exports.listColorContinuous = listColorContinuous;
|
|
823
2266
|
exports.listColorWithAlpha = listColorWithAlpha;
|
|
824
2267
|
exports.logToCsv = logToCsv;
|
|
825
2268
|
exports.logsTotalValue = logsTotalValue;
|
|
826
2269
|
exports.lollipopChartPlugin = lollipopChartPlugin;
|
|
2270
|
+
exports.missingTermIds = missingTermIds;
|
|
827
2271
|
exports.namedContributors = namedContributors;
|
|
2272
|
+
exports.nodeBorderColours = nodeBorderColours;
|
|
2273
|
+
exports.nodeColours = nodeColours;
|
|
2274
|
+
exports.nonLCAIndicatorsId = nonLCAIndicatorsId;
|
|
828
2275
|
exports.nonZeroContributors = nonZeroContributors;
|
|
829
2276
|
exports.parseColor = parseColor;
|
|
830
2277
|
exports.parseContributions = parseContributions;
|
|
2278
|
+
exports.registerChart = registerChart;
|
|
2279
|
+
exports.sumIndicators = sumIndicators;
|
|
831
2280
|
exports.sumValues = sumValues;
|
|
832
2281
|
exports.toLine = toLine;
|
|
833
2282
|
exports.valueRatio = valueRatio;
|