Creating area chart coloring the part of overflowed values with other color

delphi, teechart

Solution

There is a tool called TSeriesBandTool which can do this with a little bit of work.

Add an extra series and make it identical as your series, but with a maximum of your limit.

This is how it looks in the Tools editor :

It will fill the difference between the two series with a color of your choice.

Update

Made a proof of concept, see image.

Added a third Series and a second TSeriesBandTool to fill the bottom part with a second color. I'm sure it's possible to do with less code, but this is good enough for a demonstration.

Update 2

Made some code refreshments.

Update 2.5

Now handles arbitrary X-values as well, not only integers.

Here is the code :

uses Series, TeeTools, TeeSeriesBandTool;

Procedure DrawLimitAreaChart(S1, S2, S3: TLineSeries; YLimit: Double;
  BT1, BT2: TSeriesBandTool; OutlineWidth: Integer;
  OutlineCL, TopCl, BottomCL: TColor);
Var
  i: Integer;
  iX: Double;
Begin
  S1.LinePen.Width := OutlineWidth;
  S1.Color := OutlineCL;
  S2.Color := OutlineCL;
  S3.Color := OutlineCL;
  for i := 0 to S1.Count - 1 do
    if (S1.YValue[i] > YLimit) then
    begin
      if (i > 0) and (S1.YValue[i - 1] < YLimit) then
      begin // Last point below limit
        iX := (S1.XValue[i] - S1.XValue[i - 1]) * (YLimit - S1.YValue[i - 1]) /
          (S1.YValue[i] - S1.YValue[i - 1]) + S1.XValue[i - 1];
        S2.AddXY(iX, YLimit);
        if (i < S1.Count - 1) then
          Continue;
      end;
      S2.AddXY(S1.XValue[i], YLimit); // Set to Ylimit
    end
    else // Below Ylimit
    begin
      if (i > 0) and (S1.YValue[i - 1] > YLimit) then
      begin // Last point above limit
        iX := (S1.XValue[i] - S1.XValue[i - 1]) * (YLimit - S1.YValue[i - 1]) /
          (S1.YValue[i] - S1.YValue[i - 1]) + S1.XValue[i - 1];
        S2.AddXY(iX, YLimit);
      end;
      S2.AddXY(S1.XValue[i], S1.YValue[i]);  // Same value
    end;

  for i := 0 to S2.Count - 1 do
  begin
    S3.AddXY(S2.XValue[i], 0.0);
  end;

  { - First TSeriesBandTool }
  BT1.Series := S1;
  BT1.Series2 := S2;
  BT1.Brush.BackColor := TopCl;
  BT1.DrawBehindSeries := True;
  BT1.Transparency := 50;

  { - Second TSeriesBandTool }
  BT2.Series := S2;
  BT2.Series2 := S3;
  BT2.Brush.BackColor := BottomCL;
  BT2.DrawBehindSeries := True;
  BT2.Transparency := 50;

End;

procedure TForm1.Button1Click(Sender: TObject);
const
  // Example data
  YVAL: array [0 .. 10] of Double = (10,40,45,20,48,5,47,47,47,47,30);
var
  i: Integer;
  YLimit: Double;
  S1, S2, S3: TLineSeries;
begin
  Chart1.SeriesList.Clear;
  S1 := TLineSeries.Create(Self); // Contains real data
  Chart1.AddSeries(S1);
  S2 := TLineSeries.Create(Self); // Data below Ylimit
  Chart1.AddSeries(S2);
  S3 := TLineSeries.Create(Self); // baseline data
  Chart1.AddSeries(S3);
  for i := 0 to 10 do
    S1.AddXY(i, YVAL[i]);
  YLimit := S1.MaxYValue * 0.75;
  DrawLimitAreaChart(S1, S2, S3, YLimit, ChartTool1, ChartTool2, 2, clBlack,
    TColor($0024FF){clOrangeRed}, TColor($FF4D4D){clNeonBlue});

end;

So what the code does :

Fills in the X,Y data into series[0]. Calls `DrawLimitAreaChart` procedure where a new series is calculated from series[0] such that no part is above the Ylimit. Finally a third series is applied as a baseline to the second series. The three curves together with the two `TSeriesBandTools` now forms a two color region area chart.

Update 3

As per request here is a chart example with the X axis as time. The code for doing this is in my comment below.

Problem

Can you help me creating the area chart, where the part (top) of chart has other color? I would like to indicate that the values over any number value are critical. This is a screenshot: I've created two area charts but as you can see, each chart has own left axis. The best solution would be coloring chart above particular values.

Original source