-1

I have a line chart with many lines inside, the legend is displayed below the chart area. The problem is that the legend takes a lot of space vertically, and this space is consumed from the fix total height of the chart. In extreme cases, there is no space left for the chart itself. Is there an option to have a fix size for the chart area, and add the legend's height in addition to this? Thanks in advance!

2 Answers2

0

You can try to mess around with these values. Here's an example:

<kendo-chart-legend 
    [labels]="{font:'8pt sans-serif'}" 
    [position]="'start" 
    [orientation]="'vertical'">
</kendo-chart-legend>

Full example with Input Params.

HTML:

<kendo-chart (render)="onRender($event)">
    <kendo-chart-legend 
        [labels]="{font:'12pt sans-serif'}" 
        [position]="legendPosition" 
        [orientation]="legendOrientation">
    </kendo-chart-legend>
    <kendo-chart-title 
        [text]="titleDescription" 
        font="12pt sans-serif"
        [align]="titleAlign">
    </kendo-chart-title>
    <kendo-chart-tooltip [shared]="sharedTooltip" [format]="formatTooltip"></kendo-chart-tooltip>
    <kendo-chart-category-axis>
        <kendo-chart-category-axis-item
            name="categoryAxis"
            [labels]="{font:'10pt sans-serif'}"
            [visible]="labelStep !== 0">
            <kendo-chart-category-axis-item-labels [step]="labelStep" [content]="labelContentString" [rotation]="labelRotation"></kendo-chart-category-axis-item-labels>
        </kendo-chart-category-axis-item>
    </kendo-chart-category-axis>

    <kendo-chart-value-axis>
        <kendo-chart-value-axis-item 
            name="valueAxis"
            [plotBands]="plotBands"
            [labels]="{font:'12pt sans-serif'}">
            <kendo-chart-value-axis-item-labels [content]="labelContentString"></kendo-chart-value-axis-item-labels>
        </kendo-chart-value-axis-item>
    </kendo-chart-value-axis>

    <kendo-chart-series>
        <ng-container *ngIf="groupByField">
            <kendo-chart-series-item *ngFor="let item of series"
                [type]="type"
                [data]="item.items"
                [name]="item.value"
                [markers]="{visible: useMarkers}"
                field="value"
                categoryField="label">
            </kendo-chart-series-item>
        </ng-container>
        <ng-container *ngIf="!groupByField">
            <kendo-chart-series-item
                [type]="type"
                [data]="series"
                [markers]="{visible: useMarkers}"
                field="value"
                categoryField="label">
            </kendo-chart-series-item>
        </ng-container>
    </kendo-chart-series>
</kendo-chart>

Component:

@Input() public data: PentahoResponse;
@Input() public type: string;
@Input() public groupByField: string;
@Input() public labelField: string;
@Input() public valueField: string;
@Input() public excludeValues: Array<{ field: string, value: string }> = [];

@Input() public titleDescription: string = ""; // text (disabled if left empty)
@Input() public titleAlign: string = "start"; // start middle end
@Input() public legendPosition: string; // top bottom left right
@Input() public legendOrientation: string; // horizontal vertical
@Input() public formatTooltip: string = "{0}"; // {0} <-- value
@Input() public sharedTooltip: boolean = false; // false true
@Input() public useMarkers: boolean = true; // false true
@Input() public preferredLabelAmount: number = 15;
@Input() public labelRotation: number;
@Input() public lines: Array<{ value: number, color: string, opacity?: number }>;
private series: any[] = [];
private labelStep: number = 1;

constructor(
    @Inject(AppService) private readonly appService: AppService
) { }

public onRender(args) {
    if (this.lines && this.type === "line") {
        const chart = args.sender.instance; // Remove ".instance" when upgraded

        // get the axes
        const valueAxis = chart.findAxisByName("valueAxis");
        const categoryAxis = chart.findAxisByName("categoryAxis");

        // get the coordinates of the entire category axis range
        const range = categoryAxis.range();
        const categorySlot = categoryAxis.slot(range.min, range.max);

        const group = new Group();
        this.lines.forEach((plotLine) => {
            // get the coordinates of the value at which the plot band will be rendered
            const valueSlot = valueAxis.slot(plotLine.value);

            // draw the plot band based on the found coordinates
            const line = new Path({
                stroke: {
                    color: plotLine.color,
                    width: 3,
                    opacity: plotLine.opacity !== undefined ? plotLine.opacity : 1
                }
            }).moveTo(valueSlot.origin)
                .lineTo(categorySlot.topRight().x, valueSlot.origin.y);

            group.append(line);
        });

        // Draw on the Chart surface to overlay the series
        chart.surface.draw(group);
    }
}

private getData() {
    const metadata = this.data.metadata;
    let data = this.data.resultset;
    this.excludeValues.forEach((exclude) => {
        const fieldIndex: number = _.findIndex(metadata, (x) => x.colName === exclude.field);
        if (fieldIndex !== -1) {
            data = _.filter(data, (x) => x[fieldIndex] !== exclude.value);
        }
    });
    const labelIndex = _.findIndex(metadata, (x) => x.colName === this.labelField);
    const valueIndex = _.findIndex(metadata, (x) => x.colName === this.valueField);
    const groupByFieldIndex = _.findIndex(metadata, (x) => x.colName === this.groupByField);

    data.forEach((row) => {
        this.series.push({
            groupBy: groupByFieldIndex !== -1 ? row[groupByFieldIndex] : row[labelIndex],
            value: row[valueIndex],
            label: row[labelIndex],
            groupByKey: groupByFieldIndex !== -1 ? row[groupByFieldIndex] : row[labelIndex],
            valueKey: row[valueIndex],
            labelKey: row[labelIndex]
        });
    });
    this.series = this.groupByField !== undefined ? groupBy(this.series, [{ field: "groupBy" }]) : this.series;
}

private labelContentString = (e: any) => {
    if (e.value instanceof Date) {
        return moment(e.value).format("L LT");
    } else if (e.value instanceof String) {
        return this.appService.translate(e.value);
    }
    return e.value;
}

public ngOnInit() {
    this.getData();
    if (this.preferredLabelAmount > 0) {
        let maxTotalItems = 0;
        if (this.groupByField !== undefined) {
            this.series.forEach((serie: GroupResult) => {
                if (maxTotalItems < serie.items.length) {
                    maxTotalItems = serie.items.length;
                }
            });
        } else {
            maxTotalItems = this.series.length;
        }
        this.labelStep = Math.round(maxTotalItems / this.preferredLabelAmount);
    } else if (this.preferredLabelAmount === 0) {
        this.labelStep = 0;
    }
}

https://www.telerik.com/kendo-angular-ui/components/charts/api/Legend/

https://www.telerik.com/kendo-angular-ui/components/charts/api/LegendLabels/

https://www.telerik.com/kendo-angular-ui/components/charts/api/LegendItem/

https://www.telerik.com/kendo-angular-ui/components/charts/api/LegendMarkers/

Full API here.

Joel
  • 5,732
  • 4
  • 37
  • 65
  • Sorry, this didn't help. I can spare some space with decreasing the font, but it is not enough. Sometimes I have ~ 100 lines on the chart, in such cases, the legend is ~400px (I know, you can't even really distinguish the lines, but that's a different issue). In other cases, only 1 or 2. So finally, I solved this by traversing the SVG's DOM, recognizing the items in the legend, and checking their positions. Based on this, I can calculate the height required for the legend, and increase the height of the chart with this. – Tamás Mészáros Feb 14 '19 at 08:15
0

For a similar situation I just set the visible false for the legend of the chart in question and used a new chart widget to just display a legend.

  legend: {
    visible: false
  }

For just displaying a legend

$("#legendChart").kendoChart({
        legend: {
            position: "top",
            labels: {
                padding: {
                    left: paddingLeftLegendLabel
                }
            }
        },
        series: [
          { name: LowName },
          { name: MediumName },
          { name: HighName },
        ],
        seriesColors: ['#DCD267', '#DC8C67', '#DC6967'],
        chartArea: {
            height: 35
        },
        axisDefaults: {
            visible: false,
            majorGridLines: {
                visible: false
            }
        },
    });