Stacked bar chart problems

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Stacked bar chart problems

Post by Errol » Wed Feb 10, 2016 8:27 am

I am currently using stacked bar charts to represent lithology down a well. I formerly used the mbSelfStack mode, but this does not allow positioning of the bar chart on the horizontal axis, so now I am using the mbStacked mode. The data source is a memory table, IntervalQuery, which records the Strata Top, Strata Bottom, Rock Type Code and Color of each lithology down the well. I step through IntervalQuery and assign each layer to a new series, as shown in the code below.

However, when I call Chart.RefreshData, I get n x n layers overlying each other (where n is the number of layers). How do I control RefreshData so only one chart layer is drawn for each record of IntervalQuery? I have attached a diagram showing how a nine-layer well (one layer missing) should look (achieved using mbSelfStack) and how it looks using mbStacked.

Thanks in advance

Errol

Code: Select all

  procedure AddRockSeries(AXValue,AYValue:double;ALabel:string;AColor:integer);
  var
    LTitle,LXUnitType,LYUnitType: string;

  begin
    LTitle := IntToStr(owner.Chart.SeriesList.Count);
    if (SeriesListB.IndexOf(LTitle) = -1) then
      SeriesListB.AddObject(LTitle,TUnitBarSeries.Create(Owner));

    iIndex := SeriesListB.IndexOf(LTitle);
    with TBarSeries(SeriesListB.Objects[iIndex]) as TBarSeries do
    begin
      bMarksVisible := LMarksVisible;
      MultiBar := mbStacked;
      CustomBarWidth := LBarWidth;
      MarksLocation := mlCenter;
      MarksOnBar := True;
      ShowInLegend := False;
      XValues.ValueSource := 'ProfileDistance';
      YValues.ValueSource := 'Depth';
      XLabelsSource := 'Rock Type Code;  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
      HorizAxis := aBottomAxis;
      ParentChart := self.Owner.Chart;
      DataSource := IntervalQuery;
      AddXY(AXValue,AYValue,ALabel,AColor);
    end;
  end;
Attachments
Lithology_using_mbStacked.png
How the lithology appears using mbStacked
Lithology_using_mbStacked.png (31.29 KiB) Viewed 25431 times
Lithology_using_mbSelfStack.png
How the lithology should look
Lithology_using_mbSelfStack.png (25.05 KiB) Viewed 25407 times

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Wed Feb 10, 2016 11:58 am

Hello,

I see you are adding values to the series manually through the AddXY() function and also assigning a DataSource. I'd choose of the two methods.
Here an example using DataSource simplifying and completing your code without unknown references:

Code: Select all

uses Series;

var myTable: TTable;

procedure TForm1.FormCreate(Sender: TObject);

  procedure AddRockSeries;
  begin
    with DBChart1.AddSeries(TBarSeries) as TBarSeries do
    begin
      Marks.Visible := true;
      MultiBar := mbSelfStack;
      CustomBarWidth := 100;
      MarksLocation := mlCenter;
      MarksOnBar := True;
      ShowInLegend := False;
      XValues.ValueSource := 'ProfileDistance';
      YValues.ValueSource := 'Depth';
      XLabelsSource := 'Rock Type Code';  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
      HorizAxis := aBottomAxis;
      DataSource := myTable;
      //AddXY(AXValue,AYValue,ALabel,AColor);
    end;
  end;

var i: Integer;
begin
  myTable:=TTable.Create(Self);
  with myTable do
  begin
    FieldDefs.Add('ProfileDistance', ftInteger);
    FieldDefs.Add('Depth', ftFloat);
    FieldDefs.Add('Rock Type Code', ftInteger);
    FieldDefs.Add('Foreground', ftInteger);

    TableName:='MyValues';
    CreateTable;
    EmptyTable;
    Open;

    for i:=0 to 5 do
    begin
      Append;
      FieldByName('ProfileDistance').AsFloat:=0;
      FieldByName('Depth').AsFloat:=5+random*5;
      FieldByName('Rock Type Code').AsInteger:=Round(random*50);
      FieldByName('Foreground').AsInteger:=RGB(Round(random*255), Round(random*255), Round(random*255));
      Post;
    end;
    Close;
  end;

  DBChart1.View3D:=false;

  myTable.Open;

  AddRockSeries;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DBChart1.RefreshData;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Thu Feb 11, 2016 2:21 am

Good afternoon Yeray

I am unable to reproduce a stacked bar chart using the code you have suggested. I have attached the actual code I have used. As TTable seemed to be always interpreted as a paradox table with some network error, I have used a TkbmMemTable memory table.

I need to use the mbStacked mode to be able to specify the horizontal position of the bar, but your example used the SelfStack mode. However, I have tried both options, but nothing is displayed when I click on Button1. Have you any ideas why this does not work.

Best regards

Errol

Code: Select all

unit StackChart;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TeeGDIPlus, ExtCtrls, TeeProcs, TeEngine, Chart, StdCtrls, DB,
  Series, DBChart, kbmMemTable;

type
  TForm1 = class(TForm)
    Button1: TButton;
    DBChart1: TDBChart;
 
    procedure Button1Click(Sender: TObject);
    procedure Form1Create(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  myTable: TkbmMemTable;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  DBChart1.RefreshData;
end;

procedure TForm1.Form1Create(Sender: TObject);

  procedure AddRockSeries;
  begin
    with DBChart1.AddSeries(TBarSeries) as TBarSeries do
    begin
      Marks.Visible := true;
//      MultiBar := mbSelfStack;
      MultiBar := mbStacked;
      CustomBarWidth := 100;
      MarksLocation := mlCenter;
      MarksOnBar := True;
      ShowInLegend := False;
      XValues.ValueSource := 'ProfileDistance';
      YValues.ValueSource := 'Depth';
      XLabelsSource := 'Rock Type Code';  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
      HorizAxis := aBottomAxis;
      DataSource := myTable;
      //AddXY(AXValue,AYValue,ALabel,AColor);
    end;
  end;

var
  i: Integer;

begin
  myTable:=TkbmMemTable.Create(Self);
  with myTable do
  begin
    FieldDefs.Add('ProfileDistance', ftInteger);
    FieldDefs.Add('Depth', ftFloat);
    FieldDefs.Add('Rock Type Code', ftInteger);
    FieldDefs.Add('Foreground', ftInteger);

    CreateTable;
    EmptyTable;
    Open;

    for i:=0 to 5 do
    begin
      Append;
      FieldByName('ProfileDistance').AsFloat:=0;
      FieldByName('Depth').AsFloat:=5+random*5;
      FieldByName('Rock Type Code').AsInteger:=Round(random*50);
      FieldByName('Foreground').AsInteger:=RGB(Round(random*255), Round(random*255), Round(random*255));
      Post;
    end;
    Close;
  end;

  DBChart1.View3D:=false;

  myTable.Open;

  AddRockSeries;
end;


end.

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Thu Feb 11, 2016 2:36 pm

Hello Errol,
Errol wrote:I need to use the mbStacked mode to be able to specify the horizontal position of the bar, but your example used the SelfStack mode.
I understand you only want a single column with stacked values, is this correct?

- mbStacked mode stacks values from different Bar series with the same XValue. To have a single column you'd need to have x Bar series with a single value. Assigning a table as a series datasource loads all the values from the table to that series. So to have a single stacked column you'd need to have x tables with a single row.

- mbSelfStack mode stacks all the values from a Bar series. If the only problem is with the position of the stacked column, maybe you could try to play with the bottom axis scale. What about this?

Code: Select all

  DBChart1.Axes.Bottom.SetMinMax(-0.5, 1);
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Thu Feb 11, 2016 8:24 pm

Hello Yeray

Thanks for your reply. I wish to develop a graph with many stacked bars at specified horizontal positions, corresponding to the position of wells along a profile line. At present, this is possible only by using mbStacked. I find the TeeChart code difficult to understand, but I suspected that I would have to create a separate table containing a single row for each layer of each stacked bar, a fairly tedious process.

I do not understand why the mbSelfStack option was developed without allowing the bars to be positioned on the horizontal axis, which is available with mbStacked. This functionality has been requested for some time now, and I am deeply disappointed that this is not yet available. Self stacking is the logical way to handle stacked bar charts for many data streams, and it should have full functionality.

Thanks and regards

Errol

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Fri Feb 12, 2016 11:59 am

Hello Errol,
Errol wrote:I do not understand why the mbSelfStack option was developed without allowing the bars to be positioned on the horizontal axis, which is available with mbStacked.
As the other series, the TBarSeries has XValues and YValues. In mbSelfStack, the YValues are stacked in the same column and the XValues are ignored. The SeriesIndex is used to set the position of the series in the horizontal axis. See the MinXValue function:

Code: Select all

Function TBarSeries.MinXValue:Double;
Begin
  if MultiBar=mbSelfStack then
     result:=SeriesIndex
  else
     result:=inherited MinXValue;
end;
You could use a custom series inheriting from TBarSeries to make it use the first XValue in the series as the position in the horizontal axis. Ie:

Code: Select all

type TMyBarSeries = class(TBarSeries)
  public
    Function MinXValue:Double; override;
end;

Function TMyBarSeries.MinXValue:Double;
Begin
  result:=XValues[0];
end;
Errol wrote:This functionality has been requested for some time now, and I am deeply disappointed that this is not yet available. Self stacking is the logical way to handle stacked bar charts for many data streams, and it should have full functionality.
Can you point us to where it was requested? It could be a feature request.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Sat Feb 13, 2016 12:15 am

Hello Yeray

Thank you for your suggestions regarding developing a custom series to set the position of a self stacked bar chart on the horizontal axis. I have previously attempted to develop a custom series without success, but your suggestions may help, so I will continue with this.

The request for controlling the x-position of a (vertical) self-stacked bar is Bug 646, made on 19-Mar-2014. My name is not associated with it, as the request was made in response to an email conversation.

Thanks and regards

Errol

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Mon Feb 15, 2016 2:16 pm

Hello Errol,

I've added a project with the mentioned bug-fix attempt in the public tracker:
http://bugs.teechart.net/show_bug.cgi?id=646

Note you can add your mail to the CC list to be automatically notified when an update arrives.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Thu Feb 18, 2016 6:18 am

Thank you Yeray for your suggestions. Using MinXValue worked a treat, as you can see in the attached image. However, I have a few small problems that I am still having problems with.

Problem 1. Numeric labels on the x-axis
I am using the following code snippet, but this does not put labels on the axis (although does write the correct title)

Code: Select all

      DataSource := IntervalQuery;  // after parent chart
      XLabelDisplay := ldField
      YLabelDisplay := ldField;    
      MultiBar := mbSelfStack;
      CustomHorizAxis := tmpHorizAxis;
      CustomHorizAxis.Labels := True;

      if SameText(LIndepField,'Elevation') then
        YPosition := LWellElev
      else
        YPosition := 0;

      XValues.ValueSource := 'Cross-Section Distance';
      YValues.ValueSource := self.Owner.IndependentField;
      XLabelsSource := aFldCode;  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
Problem 2. Auto-Scale on the y-Axis
When I plot data against elevation, it does not auto-scale from the highest well elevation to the deepest well elevation, but starts at the highest base of the first bar segment from the top. The code uses YPosition, set to the elevation of each well, and an override version of PointOrigin

Code: Select all

function PointOrigin(ValueIndex: Integer; SumAll: Boolean): Double; override;

function TUnitBarSeries.PointOrigin(ValueIndex: Integer;
  SumAll: Boolean): Double;
begin
  Result := inherited PointOrigin(ValueIndex, SumAll);
  if not SumAll then
    Result := Result + YPosition;
end;
Can you suggest a fix to this problem?

Problem 3 Titles for each bar
I would like to write the well name above each well, or perhaps above the top axis or below the bottom axis. D oyou have any ideas on how to do this?

Thanks and regards

Errol
Attachments
BarChart.png
BarChart.png (37.44 KiB) Viewed 25322 times

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Thu Feb 18, 2016 9:33 am

Errol wrote:Problem 1. Numeric labels on the x-axis
I am using the following code snippet, but this does not put labels on the axis (although does write the correct title)
Could you please modify the example here or arrange a simple example project we can run as-is to reproduce the problem here?
Thanks in advance.
Errol wrote:Problem 2. Auto-Scale on the y-Axis
When I plot data against elevation, it does not auto-scale from the highest well elevation to the deepest well elevation, but starts at the highest base of the first bar segment from the top. The code uses YPosition, set to the elevation of each well, and an override version of PointOrigin
[...]
Can you suggest a fix to this problem?
I don't see what YPosition exactly is in that code.
A simple example would help us to focus on the issue.
Errol wrote:Problem 3 Titles for each bar
I would like to write the well name above each well, or perhaps above the top axis or below the bottom axis. D oyou have any ideas on how to do this?
You may want to try to use TAnnotationTools to do this.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Fri Feb 19, 2016 4:57 am

Here is some test code to illustrate the problems I am experiencing. This is written using a memory table (kbmmemtable) as I have problems using TTable. This shows that the horizontal axis labels are the first mark for each chart, not numerical values of ProfileDistance. Also the automatic scale on the vertical axis always starts at the second stratum with the highest elevation.

I look forward to your suggestions.

Thanks and regards

Errol

Code: Select all

unit StackChart;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TeeGDIPlus, ExtCtrls, TeeProcs, TeEngine, Chart, StdCtrls, DB,
  Series, DBChart, kbmMemTable, teetools;

type
  TForm1 = class;

  TForm1 = class(TForm)
    Button1: TButton;
    DBChart1: TDBChart;
    Button2: TButton;

    procedure Button1Click(Sender: TObject);
    procedure Form1Create(Sender: TObject);
    procedure RegenerateChart(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyBarSeries = class(TBarSeries)
  private
    fYPosition: double;
    procedure SetYPosition(const Value: Double);
  public
    property YPosition: Double read fYPosition write SetYPosition;
    function MinXValue:Double; override;
    function PointOrigin(ValueIndex: Integer;  SumAll: Boolean): Double; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Form1Create(Sender: TObject);
var
  i,j,iRTC: Integer;
  RTC, LTitle: string;
  R,G,B,iIndex: integer;
  DragTool: TDragMarksTool;
  LProfDist,LWellElev: double;
  MyTable: TkbmMemTable;
  SeriesListB: TStringList;

  procedure AddRockSeries;

  begin
    LTitle := IntToStr(j);
    if (SeriesListB.IndexOf(LTitle) = -1) then
      SeriesListB.AddObject(LTitle,TMyBarSeries.Create(Owner));

    iIndex := SeriesListB.IndexOf(LTitle);

//    with DBChart1.AddSeries(TMyBarSeries) as TBarSeries do
    with TMyBarSeries(SeriesListB.Objects[iIndex]) do
    begin
      Marks.Visible := true;
      MultiBar := mbSelfStack;
      CustomBarWidth := 60;
      MarksLocation := mlCenter;
      MarksOnBar := True;
      ShowInLegend := False;
      ParentChart := DBChart1;
      DataSource := myTable;
      YPosition := LWellElev;
      XValues.ValueSource := 'ProfileDistance';
      YValues.ValueSource := 'Depth';
      XLabelsSource := 'Rock Type Code';  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
      HorizAxis := aBottomAxis;
    end;
  end;

begin
  DBChart1.ClearChart;
  DragTool := DBChart1.Tools.Add(TDragMarksTool) as TDragMarksTool;
  DragTool.Active := true;
  DBChart1.View3D:=false;
  DBChart1.LeftAxis.Inverted := False;
  DBChart1.BottomAxis.Title.Caption := 'Profile Distance';
  DBChart1.LeftAxis.Title.Caption := 'Elevation';
  SeriesListB := TStringList.Create;

  for j := 1 to 3 do
  begin
    myTable := TkbmMemTable.Create(Owner);

    with myTable do
    begin
      FieldDefs.Add('ProfileDistance', ftInteger,0,false);
      FieldDefs.Add('Depth', ftFloat,0,false);
      FieldDefs.Add('Rock Type Code', ftString,10,false);
      FieldDefs.Add('Foreground', ftInteger,0,false);
      CreateTable;
      EmptyTable;
      Open;
      LProfDist := random*5000;
      LWellElev := random*500;
      for i:=0 to 5 do
      begin
        Append;
        FieldByName('ProfileDistance').AsFloat:=LProfDist;
        FieldByName('Depth').AsFloat:= -(100+random*100);  // negative
        case i of
          0: RTC := IntToStr(j)+'-'+'BAS';
          1: RTC := IntToStr(j)+'-'+'PYR';
          2: RTC := IntToStr(j)+'-'+'NO';
          3: RTC := IntToStr(j)+'-'+'TUF';
          4: RTC := IntToStr(j)+'-'+'PYR';
          5: RTC := IntToStr(j)+'-'+'MD';
        end;

        FieldByName('Rock Type Code').AsString := RTC;
        R := Round(random*255);
        G := Round(random*255);
        B := Round(random*255);
        FieldByName('Foreground').AsInteger:=RGB(R,G,B);
        Post;
      end;
      AddRockSeries;
    end;
  end;
end;

procedure TForm1.RegenerateChart(Sender: TObject);
begin
  Form1Create(owner);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DBChart1.RefreshData;
end;

Function TMyBarSeries.MinXValue:Double;
Begin
  result:=XValues[0];
end;

procedure TMyBarSeries.SetYPosition(const Value: Double);
begin
  if FYPosition <> Value then
  begin
    FYPosition := Value;
    Repaint;
  end;
end;

function TMyBarSeries.PointOrigin(ValueIndex: Integer;
  SumAll: Boolean): Double;
begin
  Result := inherited PointOrigin(ValueIndex, SumAll);
  if not SumAll then
    Result := Result + YPosition;
end;

end.

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Fri Feb 19, 2016 9:00 am

Hello Errol,
Errol wrote:Here is some test code to illustrate the problems I am experiencing. This is written using a memory table (kbmmemtable) as I have problems using TTable.
Thanks. I don't have kbmmemtable but I can switch that for a TTable and it works without problems for me here. This is what I get:
first.png
first.png (19.42 KiB) Viewed 25289 times
Errol wrote: This shows that the horizontal axis labels are the first mark for each chart, not numerical values of ProfileDistance.
Adding this I think makes it to behave as you wish:

Code: Select all

DBChart1.Axes.Bottom.LabelStyle:=talPointValue;
PointValue.png
PointValue.png (19.06 KiB) Viewed 25291 times
Errol wrote: Also the automatic scale on the vertical axis always starts at the second stratum with the highest elevation.
You could override the MaxYValue function also to get it right:

Code: Select all

function TMyBarSeries.MaxYValue:Double;
Begin
  result:=YPosition;
end;
MaxYValue.png
MaxYValue.png (19.14 KiB) Viewed 25294 times
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Sun Feb 21, 2016 2:47 am

Good afternoon Yeray

Thank you for the MaxYValue suggestion to correct the autoscale. However, for the labels on the x-axis, I prefer a normal axis label format (e.g. every 200 from say 1800 to 4000, rather than labelling the position of each bar chart. Is this possible?

I am also having some problems with the annotation tools to write the well name above each bar chart. I was hoping for series-dependent annotation. I seems quite difficult to write using a chart property, especially keeping the labels aligned when zooming or scrolling. Can you point me to code examples that deal with this.

Thanks and regards

Errol

Errol
Newbie
Newbie
Posts: 75
Joined: Thu Jul 11, 2013 12:00 am

Re: Stacked bar chart problems

Post by Errol » Mon Feb 22, 2016 4:03 am

Hello Yeray

Nearly there. I have annotations working but they do not appear until I either mouse over a chart or press Refresh. How do I get these to draw automatically. Also, can I remove the box around the text and just show the name, with no background.

The annotations are also lined up to the left of the bar chart, not the centre. Is there any easy way to automatically centre these?

I have introduced a ToolCount parameter, as I will have other tools on the chart.

Looking forward to your comments

Errol

Code: Select all

unit StackChart;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TeeGDIPlus, ExtCtrls, TeeProcs, TeEngine, Chart, StdCtrls, DB,
  Series, DBChart, kbmMemTable, teetools,contnrs;

type
  TForm1 = class;

  TForm1 = class(TForm)
    Button1: TButton;
    DBChart1: TDBChart;
    Button2: TButton;

    procedure Button1Click(Sender: TObject);
    procedure Form1Create(Sender: TObject);
    procedure RegenerateChart(Sender: TObject);
    procedure DBChart1AfterDraw(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyBarSeries = class(TBarSeries)
  private
    fYPosition: double;
    procedure SetYPosition(const Value: Double);
  public
    property YPosition: Double read fYPosition write SetYPosition;
    function MinXValue:Double; override;
    function PointOrigin(ValueIndex: Integer;  SumAll: Boolean): Double; override;
    function MaxYValue:Double; override;
  end;

var
  Form1: TForm1;
  SeriesListB: TStringList;
  WellElev,ProfDist: double;
  WellName: string;
  MyTable: TkbmMemTable;

implementation

{$R *.dfm}

procedure TForm1.DBChart1AfterDraw(Sender: TObject);
var
  j: integer;

begin
  for j := 0 to 2 do
    with (DBChart1.Tools[j] as TAnnotationTool) do
    begin
      case j of
        0: WellName := 'AT-103';
        1: WellName := 'AT-201';
        2: WellName := 'AT-604';
      end;
      Shape.CustomPosition := True;
      Text := WellName;
      Left := TMyBarSeries(SeriesListB.Objects[j]).CalcXPos(0);
  end;
end;

procedure TForm1.Form1Create(Sender: TObject);
var
  i,j,iRTC: Integer;
  RTC, LTitle: string;
  R,G,B,iIndex: integer;
  DragTool: TDragMarksTool;
  LTemp1,LTemp2: integer;

  procedure AddRockSeries;

  begin
    LTitle := IntToStr(j);
    if (SeriesListB.IndexOf(LTitle) = -1) then
      SeriesListB.AddObject(LTitle,TMyBarSeries.Create(Owner));

    iIndex := SeriesListB.IndexOf(LTitle);

//    with DBChart1.AddSeries(TMyBarSeries) as TBarSeries do
    with TMyBarSeries(SeriesListB.Objects[iIndex]) do
    begin
      Marks.Visible := true;
      MultiBar := mbSelfStack;
      CustomBarWidth := 60;
      MarksLocation := mlCenter;
      MarksOnBar := True;
      Marks.Clip := True;
      ShowInLegend := False;
      ParentChart := DBChart1;
      DataSource := myTable;
      YPosition := WellElev;
      XValues.ValueSource := 'ProfileDistance';
      YValues.ValueSource := 'Depth';
      XLabelsSource := 'Rock Type Code';  // marks!!!
      ColorSource := 'Foreground';
      VertAxis := aLeftAxis;
      HorizAxis := aBottomAxis;
    end;
  end;

begin
  DBChart1.ClearChart;
  DBChart1.Tools.Clear;
  DBChart1.View3D:=false;
  DBChart1.LeftAxis.Inverted := False;
  DBChart1.BottomAxis.Title.Caption := 'Profile Distance';
  DBChart1.LeftAxis.Title.Caption := 'Elevation';
  DBChart1.Axes.Bottom.LabelStyle:=talPointValue;
  SeriesListB := TStringList.Create;

  for j := 0 to 2 do
  begin
    case j of
      0: WellName := 'AT-103';
      1: WellName := 'AT-201';
      2: WellName := 'AT-604';
    end;
    myTable := TkbmMemTable.Create(Owner);
    with myTable do
    begin
      FieldDefs.Add('ProfileDistance', ftInteger,0,false);
      FieldDefs.Add('Depth', ftFloat,0,false);
      FieldDefs.Add('Rock Type Code', ftString,10,false);
      FieldDefs.Add('Foreground', ftInteger,0,false);
      CreateTable;
      EmptyTable;
      Open;
      ProfDist := random*5000;
      WellElev := (random*500);
      for i:=0 to 5 do
      begin
        Append;
        FieldByName('ProfileDistance').AsFloat:=ProfDist;
        FieldByName('Depth').AsFloat:= -(150+random*150);  // negative
        case i of
          0: RTC := IntToStr(j)+'-'+'BAS';
          1: RTC := IntToStr(j)+'-'+'PYR';
          2: RTC := IntToStr(j)+'-'+'NO';
          3: RTC := IntToStr(j)+'-'+'TUF';
          4: RTC := IntToStr(j)+'-'+'PYR';
          5: RTC := IntToStr(j)+'-'+'MD';
        end;

        FieldByName('Rock Type Code').AsString := RTC;
        R := Round(random*255);
        G := Round(random*255);
        B := Round(random*255);
        FieldByName('Foreground').AsInteger:=RGB(R,G,B);
        Post;
      end;
      AddRockSeries;
    end;

    DBChart1.Tools.Add(TAnnotationTool.Create(self));
    with (DBChart1.Tools[j] as TAnnotationTool) do
    begin
      Shape.CustomPosition := True;
      PositionUnits := muPixels;
      Left := TMyBarSeries(SeriesListB.Objects[j]).CalcXPos(0);
    end;
  end;
  DragTool := DBChart1.Tools.Add(TDragMarksTool) as TDragMarksTool;
  DragTool.Active := true;
end;

procedure TForm1.RegenerateChart(Sender: TObject);
begin
  Form1Create(owner);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DBChart1.RefreshData;
end;

Function TMyBarSeries.MinXValue:Double;
Begin
  result:=XValues[0];
end;

procedure TMyBarSeries.SetYPosition(const Value: Double);
begin
  if FYPosition <> Value then
  begin
    FYPosition := Value;
    Repaint;
  end;
end;

function TMyBarSeries.PointOrigin(ValueIndex: Integer;
  SumAll: Boolean): Double;
begin
  Result := inherited PointOrigin(ValueIndex, SumAll);
  if not SumAll then
    Result := Result + YPosition;
end;

function TMyBarSeries.MaxYValue:Double;
var
  LMaxY: double;
Begin
  result:=YPosition + 20;
end;

end.

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Stacked bar chart problems

Post by Yeray » Mon Feb 22, 2016 10:21 am

Hello,

Regarding the bottom axis labels, try changing the bottom axis LabelStyle from talPointValue to talValue. Ie:

Code: Select all

  DBChart1.Axes.Bottom.LabelStyle:=talValue;
Regarding the annotations, try forcing a chart repaint at the end of your OnCreate. Ie:

Code: Select all

  DBChart1.Draw;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Post Reply