amcharts5 icon indicating copy to clipboard operation
amcharts5 copied to clipboard

Axis Range support for JSON config

Open davidjb opened this issue 2 years ago • 4 comments

Looking at https://www.amcharts.com/docs/v5/concepts/serializing/ and the source code, it appears that defining Axis Ranges in JSON isn't currently supported. Having this ability would be very useful in declaratively defining charts, in the same way the parser currently does for other aspects of charts.

For note, I'm in the process of arranging a paid licence, just raising this here to ascertain if it's on the roadmap or if I've missed anything.

davidjb avatar Oct 30 '23 06:10 davidjb

What type of axis range are you trying to implement?

Is it simple guides/bands, or related to series?

martynasma avatar Oct 30 '23 10:10 martynasma

There's three different types of axis ranges I'm looking at, each for different use cases:

  1. Guides (either a single value or with endValue), with a label
  2. Series axis range (for adjusting appearance in 1 dimension)
  3. Multi-axes range (for adjusting appearance on 2 dimensions - e.g. points in a certain region or quadrant of an XY scatter)

I understand the latter isn't supported by axis ranges since they're relative to a single axis or series. The data-based Bullet configuration (https://www.amcharts.com/docs/v5/concepts/common-elements/bullets/#selectively-displaying-bullets) looks sufficient from a JavaScript perspective, but I'll have to look at adding something like an expression language to be able to parse logic out of a JSON chart configuration.

davidjb avatar Oct 31 '23 05:10 davidjb

Thanks.

None of those are available or can be easily implemented right now.

Axis ranges require special series/axis methods to be created, so it would need special handling in JSON parser.

I'm marking as a feature request, but can't provide any ETA or even promise that this will be implemented.

JSON parser in amCharts 5 was never meant as a 100% replacement for procedural code.

martynasma avatar Oct 31 '23 15:10 martynasma

Thanks @martynasma - very much understand and I appreciate your consideration. :+1:

I've implemented a workaround for use cases 1 and 2 with the following, noting that it's very much not feature-complete for everything axes ranges can do, only does basic color parsing of specific properties, and is specifically written for config where properties xAxes and yAxes values are set as #refs. Still, it might be useful for someone:

//chart.json
...
"xAxis": {
  "type": "ValueAxis",
  "settings": {
    "min": 0,
    "max": 100,
    "renderer": {
      "type": "AxisRendererX"
    }
  },
  "axisRanges": [
    {
      "range": {
        "value": 75,
        "endValue": 100
      },
      "elements": {
        "grid": {
          "stroke": "#000",
          "strokeOpacity": 0.5,
          "strokeWidth": 3
        },
        "axisFill": {
          "fill": "#00f"
        },
        "label": {
          "fill": "#060",
          "text": "75",
          "fontWeight": 500,
          "location": 0
        }
      }
    }
  ]
}
...
// chart.js
const config = JSON.parse(cfg);
const chart = await parser.parse(, { parent: root.container });

function resolveRef(ref) {
  const refId = ref.split('#')[1];
  return config.refs.findLast(obj => !!obj[refId])[refId];
}

['xAxes', 'yAxes'].forEach(axesId => {
  chart[axesId].each((axis, i) => {
    const axisData = resolveRef(config.properties[axesId][i]);
    axisData?.axisRanges.forEach(ar => {
      const rangeDataItem = axis.makeDataItem(ar.range);
      axis.createAxisRange(rangeDataItem);
      if (ar?.elements?.grid) {
        if (ar?.elements?.grid?.stroke) {
          ar.elements.grid.stroke = am5.color(ar.elements.grid.stroke);
        }
        rangeDataItem.get('grid').setAll(ar?.elements?.grid);
      }
      if (ar?.elements?.axisFill) {
        if (ar?.elements?.axisFill?.fill) {
          ar.elements.axisFill.fill = am5.color(ar.elements.axisFill.fill);
        }
        rangeDataItem.get('axisFill').setAll({ ...ar?.elements?.axisFill, visible: true });
      }
      if (ar?.elements?.label) {
        if (ar?.elements?.label?.fill) {
          ar.elements.label.fill = am5.color(ar.elements.label.fill);
        }
        rangeDataItem.get('label').setAll(ar.elements.label);
      }
    });
  });

davidjb avatar Nov 01 '23 02:11 davidjb