results-graph.component.ts 9.68 KiB
import { Component, ViewChild, AfterContentInit, OnInit } from "@angular/core";
import { Observer } from "jalhyd";
import { GraphTypeSelectComponent } from "./graph-type.component";
import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
import { PlottableData } from "../../results/plottable-data";
import { GraphType } from "../../results/graph-type";
import { ResultsComponent } from "../fixedvar-results/results.component";
@Component({
    selector: "results-graph",
    templateUrl: "./results-graph.component.html",
    styleUrls: [
        "./results-graph.component.scss"
export class ResultsGraphComponent extends ResultsComponent implements AfterContentInit, Observer {
    private _results: PlottableData;
    /** used to briefly destroy/rebuild the chart component, to refresh axis labels (@see bug #137) */
    public displayChart = true;
    @ViewChild(GraphTypeSelectComponent)
    private _graphTypeComponent: GraphTypeSelectComponent;
     * config du graphe
    public graph_type: string;
    public graph_data = {};
    public graph_options = {
        responsive: true,
        maintainAspectRatio: true,
        animation: {
            duration: 0
        legend: {
            display: false
        title: {
            display: true,
            text: ""
        elements: {
            line: {
                tension: 0
    public constructor(
        private appSetup: ApplicationSetupService,
        private intlService: I18nService
    ) {
        super();
    public set results(r: PlottableData) {
        this.forceRebuild();
        this._results = r;
        if (this._results && this._graphTypeComponent) {
            this._graphTypeComponent.selectedValue = r.graphType;
    public get availableChartAxis() {
        if (this._results) {
            return this._results.getAvailableChartAxis();
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
} } public get chartX() { if (this._results) { return this._results.chartX; } } public set chartX(X) { if (X !== this.chartX) { this._results.chartX = X; this.forceRebuild(); // refresh chart this.updateView(); } } public get chartY() { if (this._results) { return this._results.chartY; } } public set chartY(Y) { if (Y !== this.chartY) { this._results.chartY = Y; this.forceRebuild(); // refresh chart this.updateView(); } } /** * Returns a human readable description of any param / result symbol */ public getChartAxisLabel(symbol: string): string { return this._results.getChartAxisLabel(symbol); } public get uitextSelectX() { return this.intlService.localizeText("INFO_PARAMFIELD_GRAPH_SELECT_X_AXIS"); } public get uitextSelectY() { return this.intlService.localizeText("INFO_PARAMFIELD_GRAPH_SELECT_Y_AXIS"); } public updateView() { // (re)generate chart switch (this._graphTypeComponent.selectedValue) { case GraphType.Histogram: this.graph_type = "bar"; this.generateBarGraph(); break; default: this.graph_type = "scatter"; this.generateScatterGraph(); break; } } public ngAfterContentInit() { this._graphTypeComponent.addObserver(this); } /** * Calls getChartAxisLabel() and removes the symbol prefix * (cannot rebuild a clean label here)
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
*/ private axisLabelWithoutSymbol(symbol: string) { let l = this._results.getChartAxisLabel(symbol); const i = l.indexOf(": "); if (i !== -1) { l = l.substring(i + 2); } return l; } /** forces Angular to rebuild the chart @see bug #137 */ private forceRebuild() { this.displayChart = false; const that = this; setTimeout(() => { // trick that.displayChart = true; }, 10); } /** * génère les données d'un graphe de type "bar" */ private generateBarGraph() { const labs = []; const dat = []; const xSeries = this._results.getValuesSeries(this.chartX); const ySeries = this._results.getValuesSeries(this.chartY); // both series are supposed to be the same length for (let i = 0; i < xSeries.length; i++) { labs.push(xSeries[i]); dat.push(ySeries[i]); } const nDigits = this.appSetup.displayDigits; this.graph_options["scales"] = { xAxes: [{ gridLines: { offsetGridLines: true }, ticks: { precision: nDigits, callback: function(value, index, values) { return Number(value).toFixed(nDigits); } }, scaleLabel: { display: true, labelString: this.axisLabelWithoutSymbol(this.chartX) } }], yAxes: [{ scaleLabel: { display: true, labelString: this.axisLabelWithoutSymbol(this.chartY) } }] }; const that = this; this.graph_options["tooltips"] = { displayColors: false, callbacks: { title: (tooltipItems, data) => { return this.chartY + " = " + Number(tooltipItems[0].yLabel).toFixed(nDigits); }, label: (tooltipItem, data) => { const lines: string[] = []; for (const v of that._results.getVariatingParametersSymbols()) { const series = that._results.getValuesSeries(v); const line = v + " = " + series[tooltipItem.index].toFixed(nDigits); if (v === this.chartX) {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
lines.unshift(""); lines.unshift(line); } else { lines.push(line); } } return lines; } } }; this.graph_data = { labels: labs, datasets: [{ label: "", data: dat }] }; } /** * génère les données d'un graphe de type "scatter" */ private generateScatterGraph() { const dat = []; const xSeries = this._results.getValuesSeries(this.chartX); const ySeries = this._results.getValuesSeries(this.chartY); // both series are supposed to be the same length for (let i = 0; i < xSeries.length; i++) { dat.push({ x: xSeries[i], y: ySeries[i] }); } const nDigits = this.appSetup.displayDigits; this.graph_options["scales"] = { xAxes: [{ type: "linear", position: "bottom", ticks: { precision: nDigits }, scaleLabel: { display: true, labelString: this.axisLabelWithoutSymbol(this.chartX) } }], yAxes: [{ type: "linear", position: "left", ticks: { precision: nDigits }, scaleLabel: { display: true, labelString: this.axisLabelWithoutSymbol(this.chartY) } }] }; const that = this; this.graph_options["tooltips"] = { displayColors: false, callbacks: { title: (tooltipItems, data) => { return this.chartY + " = " + Number(tooltipItems[0].yLabel).toFixed(nDigits); }, label: (tooltipItem, data) => { const lines: string[] = []; for (const v of that._results.getVariatingParametersSymbols()) {
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
const series = that._results.getValuesSeries(v); const line = v + " = " + series[tooltipItem.index].toFixed(nDigits); if (v === this.chartX) { lines.unshift(""); lines.unshift(line); } else { lines.push(line); } } return lines; } } }; this.graph_data = { datasets: [{ label: "", data: dat, borderColor: "#808080", // couleur de la ligne backgroundColor: "rgba(0,0,0,0)", // couleur de remplissage sous la courbe : transparent showLine: "true" }] }; } public exportAsImage(element: HTMLDivElement) { const canvas: HTMLCanvasElement = element.querySelector("canvas"); canvas.toBlob((blob) => { saveAs(blob, "chart.png"); }); // defaults to image/png } // interface Observer update(sender: any, data: any) { if (sender instanceof GraphTypeSelectComponent) { if (data.action === "graphTypeChanged") { this._results.graphType = data.value; } } this.updateView(); } }