Multiple Axes

Most charts have two axes, X and Y. This works well as long as all the data on the chart has the same nature and can share the same scale.

But some charts contain series that show different types of data, with different units and scales. The chart below has two series that represent amounts (sales and expenses) and one that represents quantities (downloads).

Plotting all the series against a single Y axis squeezes the first two series against the bottom of the chart.

The easiest way to solve this problem and still using a single chart is to create a secondary Y axis and assign it to the axisY property of the "Downloads" series.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjChart from '@grapecity/wijmo.chart'; import { getData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create the chart let theChart = new wjChart.FlexChart('#theChart', { itemsSource: getData(), bindingX: 'country', series: [ { binding: 'sales', name: 'Sales' }, { binding: 'expenses', name: 'Expenses' }, { binding: 'downloads', name: 'Downloads', chartType: 'LineSymbols' } ], axisY: { format: 'n0,', title: 'Sales/Expenses (US$ k)', axisLine: true }, }); theChart.palette = getRandomPalette(); // // create and apply extra Y axis for 'Downloads' series let axisY2 = new wjChart.Axis(); axisY2.position = wjChart.Position.Right; axisY2.title = 'Downloads (k)'; axisY2.format = 'n0,'; axisY2.min = 0; axisY2.axisLine = true; getSeries('downloads').axisY = axisY2; // // toggle extra axis document.querySelector('#secondaryAxis').addEventListener('click', e => { getSeries('downloads').axisY = e.target.checked ? axisY2 : null; }); // // get a series by its binding function getSeries(binding) { let s = theChart.series; // for (let i = 0; i < s.length; i++) { if (s[i].binding == binding) { return s[i]; } } // return null; } } // function getRandomPalette() { let palettes = Object.getOwnPropertyNames(wjChart.Palettes) .filter(prop => typeof wjChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjChart.Palettes[palettes[rand]]; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Multiple Axes</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <label for="secondaryAxis">Secondary Y Axis</label> <input id="secondaryAxis" type="checkbox" checked="checked"> <div id="theChart"></div> </div> </body> </html> // create some random data export function getData() { let countries = 'US,Canada,Mexico,Germany,UK,France,Italy,Japan,Korea,China'.split(','), data = []; // for (let i = 0; i < countries.length; i++) { data.push({ country: countries[i], sales: 1000 + Math.random() * 10000, expenses: 1000 + Math.random() * 5000, downloads: Math.round(Math.random() * 100000) }); } // return data; } .wj-flexchart { height: 300px; } .wj-control { margin-bottom: 6px; } body { margin-bottom: 24pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import { Component, Inject, enableProdMode, NgModule, ViewChild } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { WjChartModule } from '@grapecity/wijmo.angular2.chart'; import { DataService } from './app.data'; import * as wjChart from '@grapecity/wijmo.chart'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { data: any[]; palette: any; @ViewChild('axisY') axisY: wjChart.Axis; @ViewChild('ser') ser: wjChart.Series; // constructor(@Inject(DataService) private dataService: DataService) { this.data = dataService.getData(); this.palette = this.getRandomPalette(); } // getRandomPalette() { let palettes = Object.getOwnPropertyNames(wjChart.Palettes) .filter(prop => typeof wjChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjChart.Palettes[palettes[rand]]; } // saChange(sa: HTMLInputElement) { this.ser.axisY = sa.checked ? this.axisY : null; } } // @NgModule({ imports: [FormsModule, WjChartModule, BrowserModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Multiple Axes</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <div class="form-group"> <label for="secondaryAxis">Secondary Y Axis</label> <input #sa id="secondaryAxis" type="checkbox" checked="checked" (change)="saChange(sa)"> <wj-flex-chart bindingX="country" [itemsSource]="data" [palette]="palette"> <wj-flex-chart-series binding="sales" name="Sales"></wj-flex-chart-series> <wj-flex-chart-series binding="expenses" name="Expenses"></wj-flex-chart-series> <wj-flex-chart-series #ser binding="downloads" name="Downloads" chartType="LineSymbols"> <wj-flex-chart-axis #axisY wjProperty="axisY" position="Right" title="Downloads (k)" format="n0," [min]="0" [axisLine]="true"></wj-flex-chart-axis> </wj-flex-chart-series> <wj-flex-chart-axis wjProperty="axisY" format="n0," title="Sales/Expenses (US$ k)" [axisLine]="true"></wj-flex-chart-axis> </wj-flex-chart> </div> </div> import { Injectable } from '@angular/core'; // @Injectable() export class DataService { getData() { let countries = 'US,Canada,Mexico,Germany,UK,France,Italy,Japan,Korea,China'.split(','), data = []; // for (let i = 0; i < countries.length; i++) { data.push({ country: countries[i], sales: 1000 + Math.random() * 10000, expenses: 1000 + Math.random() * 5000, downloads: Math.round(Math.random() * 100000) }); } // return data; } } body { margin-bottom: 24px; } label { margin-right: 3px; } <template> <div class="container-fluid"> <div class="form-group"> <label for="secondaryAxis">Secondary Y Axis</label> <input id="secondaryAxis" type="checkbox" checked="checked" v-on:change="saChange"> <wj-flex-chart bindingX="country" :itemsSource="data" :palette="palette" :initialized="initializeChart"> <wj-flex-chart-series binding="sales" name="Sales"></wj-flex-chart-series> <wj-flex-chart-series binding="expenses" name="Expenses"></wj-flex-chart-series> <wj-flex-chart-series binding="downloads" name="Downloads" chartType="LineSymbols"> <wj-flex-chart-axis wjProperty="axisY" position="Right" title="Downloads (k)" format="n0," :min="0" :axisLine="true"></wj-flex-chart-axis> </wj-flex-chart-series> <wj-flex-chart-axis wjProperty="axisY" format="n0," title="Sales/Expenses (US$ k)" :axisLine="true"></wj-flex-chart-axis> </wj-flex-chart> </div> </div> </template> <script> import "@grapecity/wijmo.styles/wijmo.css"; import 'bootstrap.css'; import Vue from 'vue'; import '@grapecity/wijmo.vue2.core'; import '@grapecity/wijmo.vue2.chart'; import { getData } from './data'; import * as wjChart from '@grapecity/wijmo.chart'; let App = Vue.extend({ name: 'app', data: function () { return { data: getData(), palette: this.getRandomPalette() } }, methods: { initializeChart: function(flex) { this.ser = flex.series[2]; this.axisY = this.ser.axisY; }, getRandomPalette: function() { let palettes = Object.getOwnPropertyNames(wjChart.Palettes) .filter(prop => typeof wjChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjChart.Palettes[palettes[rand]]; }, saChange: function(sa) { this.ser.axisY = sa.target.checked ? this.axisY : null; } } }) new Vue({ render: h => h(App) }).$mount('#app'); </script> <style> body { margin-bottom: 24px; } label { margin-right: 3px; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Multiple Axes</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"></div> </body> </html> export function getData() { let countries = 'US,Canada,Mexico,Germany,UK,France,Italy,Japan,Korea,China'.split(','), data = []; // for (let i = 0; i < countries.length; i++) { data.push({ country: countries[i], sales: 1000 + Math.random() * 10000, expenses: 1000 + Math.random() * 5000, downloads: Math.round(Math.random() * 100000) }); } // return data; } import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import "./app.css"; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjChart from "@grapecity/wijmo.react.chart"; import * as wjcChart from "@grapecity/wijmo.chart"; import { getData } from "./data"; class App extends React.Component { constructor(props) { super(props); this.state = { data: getData(), palette: this.getRandomPalette() }; } render() { return <div className="container-fluid"> <div className="form-group"> <label htmlFor="secondaryAxis">Secondary Y Axis</label> <input id="secondaryAxis" type="checkbox" defaultChecked onClick={this.saChange.bind(this)}/> <wjChart.FlexChart bindingX="country" initialized={this.initializeChart.bind(this)} itemsSource={this.state.data} palette={this.state.palette}> <wjChart.FlexChartSeries binding="sales" name="Sales"></wjChart.FlexChartSeries> <wjChart.FlexChartSeries binding="expenses" name="Expenses"></wjChart.FlexChartSeries> <wjChart.FlexChartSeries binding="downloads" name="Downloads" chartType="LineSymbols"> <wjChart.FlexChartAxis wjProperty="axisY" position="Right" title="Downloads (k)" format="n0," min={0} axisLine={true}> </wjChart.FlexChartAxis> </wjChart.FlexChartSeries> <wjChart.FlexChartAxis wjProperty="axisY" format="n0," title="Sales/Expenses (US$ k)" axisLine={true}> </wjChart.FlexChartAxis> </wjChart.FlexChart> </div> </div>; } initializeChart(flex) { this.ser = flex.series[2]; this.axisY = this.ser.axisY; } getRandomPalette() { let palettes = Object.getOwnPropertyNames(wjcChart.Palettes) .filter(prop => typeof wjcChart.Palettes[prop] === "object" && prop !== 'prototype'); let rand = Math.floor(Math.random() * palettes.length); // return wjcChart.Palettes[palettes[rand]]; } saChange(sa) { this.ser.axisY = sa.target.checked ? this.axisY : null; } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Grapecity Wijmo OLAP Pivot Chart Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> body { margin-bottom: 24px; } label { margin-right: 3px; } export function getData() { let countries = 'US,Canada,Mexico,Germany,UK,France,Italy,Japan,Korea,China'.split(','), data = []; // for (let i = 0; i < countries.length; i++) { data.push({ country: countries[i], sales: 1000 + Math.random() * 10000, expenses: 1000 + Math.random() * 5000, downloads: Math.round(Math.random() * 100000) }); } // return data; }