Axis Ranges

An axis range is a way to define a specific range on an axis, then use that range to override some visual properties of the series to its parts that fall into range, or put visual guides to a particular position or a stretch of the axis.

Possible uses

Axis ranges can be used for two things:

  1. Modify visual properties of part of the series, that falls into specific range, e.g. color parts of the Line series that fall below zero value in red;
  2. Mark specific values or ranges of values on an axis with a straight line and optionally labels, a.k.a. "guides".

Let's explore both uses.

Using with series

We can use them, for example, to color a chunk of a line series differently by adding a range to its Date axis. Or, fill area series with different color for sections that fall below some threshold value, using ranges on a Value axis.

Unlike axis breaks, those do not have own classes, but rather are added into axis' axisRanges which is a list of objects representing the range.

Each axis type supports ranges, albeit with different properties.

Value axis range

Let's try to look at ranges on a value axis.

Say, we have a column series and we want to fill stretches from certain value to certain value with a slightly different color.

First, to create an axis range we will use axis' method createSeriesRange(series). This method is available for each axis type. It takes a reference to particular series as a parameter, so the axis knows which series to apply range overrides to.

let range = valueAxis.createSeriesRange(series);
range.value = 300;
range.endValue = 1100;
range.contents.stroke = am4core.color("#396478");
range.contents.fill = range.contents.stroke;
var range = valueAxis.createSeriesRange(series);
range.value = 300;
range.endValue = 1100;
range.contents.stroke = am4core.color("#396478");
range.contents.fill = range.contents.stroke;
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    "id": "s1",
    // ...
  }],
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "series": "s1",
      "value": 300,
      "endValue": 1200,
      "contents": {
        "stroke": "#396478",
        "fill": "#396478"
      }
    }]
  }]
}

Now, let's dissect the above.

The value and endValue are pretty self-explanatory. It defines outer edges of the range.

NOTE For Date axis, those would instead be date and endDate, while for Category axis, we'd need to use category and endCategory.

The content part is where you define which series' properties to override for parts that fall within "start" and "end" values (or dates, or categories). The key is property, and the value is the value we want to override it with.

NOTE Important note for JSON users: range needs to be connected to series, hence the need for using id and series properties, to bind the two through a unique id.

Let's see how this looks in action:

See the Pen amCharts V4: Axis ranges (1) by amCharts (@amcharts) on CodePen.24419

Date axis range

To drive this home, let's try to apply a range to a Date axis, to change part of the line series.

As mentioned earlier, we'll use date and endDate to indicate range start and end. We'll also be setting different contents properties - ones that are relevant to line series.

See the Pen amCharts V4: Axis ranges (2) by amCharts (@amcharts) on CodePen.24419

Ranges as guides

Ranges as guides work similarly as in previous sections, in that they define start and end position on the axis.

The difference is that they do not modify sections of the series, but rather display a visual guide as a straight line or a band.

Creating a guide

Since we do not have to connect it with any series, we can use simple ranges.create() method to generate new instances of Axis ranges.

let range = valueAxis.axisRanges.create();
range.value = 300;
range.endValue = 1100;
var range = valueAxis.axisRanges.create();
range.value = 300;
range.endValue = 1100;
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "value": 300,
      "endValue": 1200
    }]
  }]
}

Setting ranges works exactly as with series-bound ranges, so we're not going to repeat the whole shebang again. Please refer the previous sections about value/endValue, date/endDate, and category/endCategory.

Configuring guide's appearance

Now, configuring appearance of the guide's appearance might seem a bit tricky, until you get a hang of it.

Properties, that are available for configuration depend on the type of axis the guide is attached to. Basically, each axis consists of smaller bits - data items that include grid elements and fill in-between.

Guides (or axis ranges) are basically axis data items, and have all the same properties and configuration options.

So, say for Value axis, we would configure a guide as a ValueAxisDataItem. For Category axis - CategoryAxisDataItem, and finally, for Date axis - DateAxisDataItem.

Don't worry if this seems a bit overwhelming, it will be much clearer in a bit.

Let's continue working with a Value axis, and take a look at ValueAxisDataItem.

Among other properties we see axisFill which we will use to set up fills, and grid which defines line elements.

As we see in class reference axisFill is an instance of AxisFill class, which has fill and fillOpacity properties.

Ranged fills / bands

Let's try to set those on our newly-created guide and see what happens:

let range = valueAxis.axisRanges.create();
range.value = 300;
range.endValue = 1100;
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.2;
var range = valueAxis.axisRanges.create();
range.value = 300;
range.endValue = 1100;
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.2;
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "value": 300,
      "endValue": 1200,
      "axisFill": {
        "fill": "#396478",
        "fillOpacity": 0.2
      }
    }]
  }]
}

See the Pen amCharts V4: Axis ranges (3) by amCharts (@amcharts) on CodePen.24419

Single lines

Now, if wanted to add straight line instead, I could use range's grid property instead, because it deals with grid lines, and that's what we want - a single line.

And, since we don't have to define an actual range, we can only set value. (no need for endValue)

Let's create two lines:

var range = valueAxis.axisRanges.create();
range.value = 1000;
range.grid.stroke = am4core.color("#396478");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;

var range2 = valueAxis.axisRanges.create();
range2.value = 500;
range2.grid.stroke = am4core.color("#A96478");
range2.grid.strokeWidth = 2;
range2.grid.strokeOpacity = 1;
var range = valueAxis.axisRanges.create();
range.value = 1000;
range.grid.stroke = am4core.color("#396478");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;

var range2 = valueAxis.axisRanges.create();
range2.value = 500;
range2.grid.stroke = am4core.color("#A96478");
range2.grid.strokeWidth = 2;
range2.grid.strokeOpacity = 1;
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "value": 1000,
      "grid": {
        "stroke": "#396478",
        "strokeWidth": 2,
        "strokeOpacity": 1
      }
    }, {
      "value": 500,
      "grid": {
        "stroke": "#A96478",
        "strokeWidth": 2,
        "strokeOpacity": 1
      }
    }]
  }]
}

See the Pen amCharts V4: Axis ranges (4) by amCharts (@amcharts) on CodePen.24419

Range labels

Adding guide labels

You may also want to add some accompanying text to your guides.

Again, we turn to axis' data item, and see it has a label property. We can use that!

Now, label is an object of type Label, which means we can do all kinds of beautiful (and nasty) things to it to make it look just like we want it to.

let range = valueAxis.axisRanges.create();
range.value = 1000;
range.grid.stroke = am4core.color("#396478");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;
range.label.inside = true;
range.label.text = "Goal";
range.label.fill = range.grid.stroke;
//range.label.align = "right";
range.label.verticalCenter = "bottom";

let range2 = valueAxis.axisRanges.create();
range2.value = 500;
range2.grid.stroke = am4core.color("#A96478");
range2.grid.strokeWidth = 2;
range2.grid.strokeOpacity = 1;
range2.label.inside = true;
range2.label.text = "Threshold";
range2.label.fill = range2.grid.stroke;
//range2.label.align = "right";
range2.label.verticalCenter = "bottom";
var range = valueAxis.axisRanges.create();
range.value = 1000;
range.grid.stroke = am4core.color("#396478");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;
range.label.inside = true;
range.label.text = "Goal";
range.label.fill = range.grid.stroke;
//range.label.align = "right";
range.label.verticalCenter = "bottom";

var range2 = valueAxis.axisRanges.create();
range2.value = 500;
range2.grid.stroke = am4core.color("#A96478");
range2.grid.strokeWidth = 2;
range2.grid.strokeOpacity = 1;
range2.label.inside = true;
range2.label.text = "Threshold";
range2.label.fill = range2.grid.stroke;
//range2.label.align = "right";
range2.label.verticalCenter = "bottom";
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "value": 1000,
      "grid": {
        "stroke": "#396478",
        "strokeWidth": 2,
        "strokeOpacity": 1
      },
      "label": {
        "inside": true,
        "text": "Goal",
        "fill": "#396478",
        "verticalCenter": "bottom"
      }
    }, {
      "value": 500,
      "grid": {
        "stroke": "#A96478",
        "strokeWidth": 2,
        "strokeOpacity": 1
      },
      "label": {
        "inside": true,
        "text": "Threshold",
        "fill": "#A96478",
        "verticalCenter": "bottom"
      }
    }]
  }]
}

See the Pen amCharts V4: Axis ranges (5) by amCharts (@amcharts) on CodePen.24419

Adding ranged label

Guides are not the only Axis range types that can make use of labels. You can add those to Axis ranges that span several values.

In such cases the label will be positioned right in the middle of the range.

Important Axis renderer will override label's horizontalCenter and verticalCenter to correctly position it in the middle. If we want to modify those properties, we'll need to set up an Adapter to override it back.

let range = valueAxis.axisRanges.create();
range.value = 0;
range.endValue = 2000;
range.label.text = "Positive";
range.label.disabled = false;
range.label.rotation = 90;
range.label.fill = am4core.color("#0c0");
range.label.adapter.add("horizontalCenter", function() {
  return "middle";
});
var range = valueAxis.axisRanges.create();
range.value = 0;
range.endValue = 2000;
range.label.text = "Positive";
range.label.disabled = false;
range.label.rotation = 90;
range.label.fill = am4core.color("#0c0");
range.label.adapter.add("horizontalCenter", function() {
  return "middle";
});
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "axisRanges": [{
      "value": 0,
      "endValue": 2000,
      "label": {
        "text": "Positive",
        "disabled": false,
        "rotation": 90,
        "fill": "#0c0",
        "adapter": {
          "horizontalCenter": function() {
            return "middle";
          }
        }
      }
    }]
  }]
}

See the Pen amCharts V4: Axis ranges (6) by amCharts (@amcharts) on CodePen.24419

Guide labels on category axis

There's one caveat about adding guide (range) labels on a Category axis.

Axis range on a Category axis will inherit data item of the category it is placed on. The label attached to this range will firstly look into data item for the text, causing it to mistakenly display category label rather then what you have set in range's text property.

At some point we might fix that, but for now a simple workaround trick will work - setting text directly on range's dataItem:

range.dataItem.text = "My custom label";
range.dataItem.text = "My custom label";
// This will not work on JSON.
// We can use adapter approach:
{
  // ...
  "yAxes": [{
    "type": "CategoryAxis",
    // ...
    "axisRanges": [{
      // ...
      "label": {
        // ...
        "adapter": {
          "text": function() {
            return "My custom label";
          }
        }
      }
    }]
  }]
}

Here's an example:

See the Pen amCharts 4: Adding labels on CategoryAxis by amCharts (@amcharts) on CodePen.24419

Range locations

Some axis types, like Date axis or Category axis, are divided in clear increments. For a Date axis it's a minimum period, like a day. For Category axis, it's a category.

When a Range goes from one increment (date or category) to another (as defined date/category and endDate/endCategory) they will span from the beginning of the start increment to the ending of the end increment.

Here's an example of a Category axis Range:

let range = categoryAxis.axisRanges.create();
range.category = "B";
range.endCategory = "D";
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.3;
var range = categoryAxis.axisRanges.create();
range.category = "B";
range.endCategory = "D";
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.3;
{
  // ...
  "xAxes": [{
    "type": "CategoryAxis",
    // ...
    "axisRanges": [{
      "category": "B",
      "endCategory": "D",
      "axisFill": {
        "fill": "#396478",
        "fillOpacity": 0.3
      }
    }]
  }]
}

Notice, how Range's fill starts from the beginning of "B" and fills all the way to the ending of "D":

See the Pen amCharts V4: Axis ranges (6) by amCharts (@amcharts) on CodePen.24419

While it is the default behavior, it is within our power to make Range start at any point within the date or category.

That's what Range has property locations for.

locations is an object, which for Date axis will have properties date and endDate, while for Category axis, it will have category and endCategory.

Those hold not actual date or category but a relative location within that specific increment. A relative location is a number between 0 (zero) and 1 (one), with the former meaning beginning and the latter the end.

So, the default locations for a Category axis Range is:

{
  "category": 0,
  "endCategory": 1
}

Which means "start at the beginning (0) of the start category and end at the ending (1) of the end category".

Let's say we want to start the fill at 20% of the "B" and end at 80% of the "D". Our locations would have to be 0.2 and 0.8 respectively:

let range = categoryAxis.axisRanges.create();
range.category = "B";
range.endCategory = "D";
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.3;
range.locations.category = 0.2;
range.locations.endCategory = 0.8;
var range = categoryAxis.axisRanges.create();
range.category = "B";
range.endCategory = "D";
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.3;
range.locations.category = 0.2;
range.locations.endCategory = 0.8;
{
  // ...
  "xAxes": [{
    "type": "CategoryAxis",
    // ...
    "axisRanges": [{
      "category": "B",
      "endCategory": "D",
      "axisFill": {
        "fill": "#396478",
        "fillOpacity": 0.3
      },
      "locations": {
        "category": 0.2,
        "endCategory": 0.8
      }
    }]
  }]
}

Let's see how that affects our Range:

See the Pen amCharts V4: Axis ranges (7) by amCharts (@amcharts) on CodePen.24419

Removing ranges

An Axis range is a full-featured object, which means it can be dispose()'d to remove it.

And since axisRanges is a List, you can just clear() to remove them all at once.

When range is removed from axisRanges it's automatically disposed.

valueAxis.axisRanges.clear();
valueAxis.axisRanges.clear();

Related demos