Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highcharts: X and Y Axis cross at zero in scatter chart

Tags:

highcharts

In highcharts, I want to plot a scatter/bubble chart where the x values range from -50 to +50, and the y values from -100 to +100. Is there a way to get the x and y axis to be plotted so that they cross at zero in the center of the chart ?

I've tried using the 'offset' parameter in the axis, which does move the axis, e.g. http://bit.ly/Xd9Z51 but this approach has the following problems:

  1. You can't set the offset parameter dynamically, so if a series updates and changes the min/max x/y values, the axis no longer cross at zero.
  2. If you zoom, the axis no longer cross at zero.
  3. It's quite hard to calculate the offsets in pixels, taking into account div size, margins, titles etc.
  4. Resizing the containing div causes the required axis offset positions to change.
like image 882
SteveP Avatar asked Feb 28 '13 16:02

SteveP


1 Answers

Solution for Highcharts v7.2+

Force some options to prevent chart from reserving space for axes, then use plugin:

(function(H) {
  H.addEvent(
    H.Axis,
    'init',
    function(e) {
      if (H.defined(e.userOptions.matchAxis)) {
        H.merge(
          true,
          e.userOptions, {
            labels: {
              reserveSpace: false
            },
            title: {
              reserveSpace: false
            },
            offset: -1,
            opposite: this.isXAxis ? true : false
          }
        );
      }
    }
  );

  H.addEvent(
    H.Chart,
    'render',
    function() {
      let chart = this;

      chart.axes.forEach(function(axis) {

        let newOffset,
          matchAxis = axis.options.matchAxis,
          translate = axis.horiz ? 'translateY' : 'translateX',
          newTranslation = {},
          offset = axis.options.offset || 0,
          crisp = axis.options.lineWidth % 2,
          otherAxes = axis.isXAxis ? chart.yAxis : chart.xAxis;

        if (H.defined(matchAxis)) {
          newOffset = otherAxes[matchAxis.index || 0].toPixels(matchAxis.value || 0, true);

          newTranslation[translate] = newOffset + offset + crisp;

          if (axis.axisGroup) {
            axis.axisGroup.animate(newTranslation);
          }
          if (axis.labelGroup) {
            axis.labelGroup.animate(newTranslation);
          }
        }
      });
    }
  );
})(Highcharts);

Demo: https://jsfiddle.net/BlackLabel/2k0bw6q3/

Plugin's API:

  • x/yAxis.matchAxis.value - set value that should be picked from the opposite axis. Defaults to 0.
  • x/yAxis.matchAxis.index - when using multiple axes, set index which opposite axis should be used for calculations. Defaults to 0.

Setting e.g. yAxis.matchAxis = {} is completely fine: https://jsfiddle.net/BlackLabel/qwyczoxb/ (because of defaults)

List of options that are forced (in plugin):

  • x/yAxis.labels.reserveSpace to false
  • x/yAxis.title.reserveSpace to false
  • x/yAxis.offset to a negative value (e.g. -50)
  • xAxis.opposite is set to true
  • yAxis.opposite is set to false

Note: Tested only with v7.2.0

Old solution (v3+)

Right now the only solution is to use offset parameter. However since Highcharts 3.0 you can control freely that property and update axis:

chart.yAxis[index].update( options );

Where options is an object with options for yAxis, including offset. To get pixel position, you can use inner function chart.xAxis[index].translate(value) (little hacky, but works). So when both are combined, see example: http://jsfiddle.net/UGpPV/6/

like image 174
Paweł Fus Avatar answered Dec 23 '22 22:12

Paweł Fus