Lazarus

Programming => Graphics and Multimedia => TAChart => Topic started by: flori on February 27, 2021, 09:15:13 am

Title: TChart start position
Post by: flori on February 27, 2021, 09:15:13 am
Does anyone know where is the EndPosition, StartPosition on my TChart? I have just position.
https://wiki.lazarus.freepascal.org/Comparing_TAChart_with_Delphi's_TeeChart_Standard
( it doesn't look like that to me)
Title: Re: TChart start position
Post by: wp on February 27, 2021, 01:26:53 pm
I don't understand what you want to say.
Title: Re: TChart start position
Post by: flori on February 27, 2021, 02:39:07 pm
I found it.
Extend: Ymax, Ymin /Xmax Xmin -start y/start x
WP> I create a small demo which use dbf. ->ID, FARM_Name, YIELD. I want to display these TChart BarSeries. As a result I get different column size(I do not understand why!) Small demo project with different behavior of TChart.
When I change Id: 1,2,3.. then I get good results.
I attached small demo.
Thanks for watching
Title: Re: TChart start position
Post by: flori on February 27, 2021, 03:07:02 pm
I think my Lazarus Chart is not working good.
I try listChartsource : as a results: different column width
X   y
1   25
2   26
4   27

When I modified: as a results: same column width
x   y
1   25
2   26
3   27


it's OK
Title: Re: TChart start position
Post by: flori on February 27, 2021, 03:32:14 pm
I create small demo2 with TChartListSource. The TChart column is different width.
I do not understand
Title: Re: TChart start position
Post by: wp on February 27, 2021, 04:38:03 pm
I could not run your demo because I do not use your report engine. (Please try to keep demos as simple as possible).

But I could see in the lfm file that the x values of the bar series are not equidistant: 2001, 2002, 2003, 2006. In its default settings, the BarSeries calculates the bar widths on the basis of BarWidthPercent in which the "percent" refers to the distance to the next (or previous?) data point. So, when the x values do not have the same distance the bars will vary in width. To adjust the bars to equal widths you must set the BarWidthStyle to bwPercentMin which looks for the smallest x value distance and calculates the widths of all bars based on this value.

Alternatively to changing the BarWidthStyle you can also add "empty" values at the missing years, 2004 and 2005:
Code: Pascal  [Select][+][-]
  1.   ListChartSourc1.Add(2001, 2, 'Farm1');
  2.   ListChartSource1.Add(2002, 50, 'Farm2');
  3.   ListChartSource1.Add(2003, 9, 'Farm4');
  4.   ListChartSource1.Add(2004, NaN);   // <--------- This is a "missing" value
  5.   ListChartSource1.Add(2005, NaN);
  6.   ListChartSource1.Add(2006, 50, 'Farm3');
You must add unit math to the uses clauses to get access to NaN ("not a number").

Title: Re: TChart start position
Post by: flori on February 27, 2021, 06:34:27 pm
WP> Thank you very much for your help!
 I create a small demo again. I just set ListChartSource.
Unfortunately, users (farmers) change the years many times. Never contain such as: 2012,2013, 2014. Usually the years is unsettled. (2008, 1988, 1999, etc) they are not sorted by year.
I set BarWidthStyle to bwPercentMin and x values of the bar series are equal. The column width is ok , but unfortunately the distance between columns are not equal. (not user friendly and does not looks good).
Thanks
Title: Re: TChart start position
Post by: flori on February 28, 2021, 08:26:15 am
WP> Thank you very much for your help!
 I create a small demo again. I just set ListChartSource.
Unfortunately, users (farmers) change the years many times. Never contain such as: 2012,2013, 2014. Usually the years is unsettled. (2008, 1988, 1999, etc) they are not sorted by year.
I set BarWidthStyle to bwPercentMin and x values of the bar series are equal. The column width is ok , but unfortunately the distance between columns are not equal. (not user friendly and does not looks good).
Thanks

Does anyone know how to solve this problem?
Many thank u!!
Title: Re: TChart start position
Post by: flori on February 28, 2021, 09:00:31 am
I could not run your demo because I do not use your report engine. (Please try to keep demos as simple as possible).

But I could see in the lfm file that the x values of the bar series are not equidistant: 2001, 2002, 2003, 2006. In its default settings, the BarSeries calculates the bar widths on the basis of BarWidthPercent in which the "percent" refers to the distance to the next (or previous?) data point. So, when the x values do not have the same distance the bars will vary in width. To adjust the bars to equal widths you must set the BarWidthStyle to bwPercentMin which looks for the smallest x value distance and calculates the widths of all bars based on this value.

Alternatively to changing the BarWidthStyle you can also add "empty" values at the missing years, 2004 and 2005:
Code: Pascal  [Select][+][-]
  1.   ListChartSourc1.Add(2001, 2, 'Farm1');
  2.   ListChartSource1.Add(2002, 50, 'Farm2');
  3.   ListChartSource1.Add(2003, 9, 'Farm4');
  4.   ListChartSource1.Add(2004, NaN);   // <--------- This is a "missing" value
  5.   ListChartSource1.Add(2005, NaN);
  6.   ListChartSource1.Add(2006, 50, 'Farm3');
You must add unit math to the uses clauses to get access to NaN ("not a number").


I try different years: The column width is ok , but unfortunately the distance between columns are not equal.
Code: Pascal  [Select][+][-]
  1.  ListChartSource1.Add(2001, 2, 'Farm1');
  2.   ListChartSource1.Add(2002, 50, 'Farm2');
  3.   ListChartSource1.Add(2003, 9, 'Farm4');
  4.  // ListChartSource1.Add(2004, 0);   // <--------- This is a "missing" value
  5.   //ListChartSource1.Add(2005, 0);
  6.   ListChartSource1.Add(2006, 50, 'Farm3');
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 09:21:31 am
I would make the labels plain consecutive integer value: 0,1,2,3...etc

Then I create an array to map those values to the year:
Code: Pascal  [Select][+][-]
  1. type
  2.   TDynIntArray = array of Integer;
  3.  
  4. constructor TForm1.Create(AOwner: TComponent);
  5.  
  6.   procedure AAdd( var AArray: TDynIntArray ; Value:Integer );
  7.   var
  8.     i : Integer;
  9.   begin
  10.     SetLength(  AArray,Length(AArray) + 1 );
  11.     i := Length(AArray);
  12.     AArray[i-1] := Value;
  13.   end;
  14.  
  15. begin
  16.   inherited Create(AOwner);
  17.   AAdd(Self.FMapXYear,2012);
  18.   AAdd(Self.FMapXYear,2013);
  19.   AAdd(Self.FMapXYear,2019);
  20.   AAdd(Self.FMapXYear,2021);
  21. end;
  22.  

After that, I set the bottom TChartAxis onMarkToText event to print the year instead of the x value:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Chart1AxisList1MarkToText(var AText: String; AMark: Double);
  2. var
  3.   i : Integer;
  4. begin
  5.   i := StrToInt( AText);
  6.   if i >= 0 then begin
  7.     AText := IntToStr(Self.FMapXYear[i]);
  8.   end;
  9. end;
  10.  

Attached is the screenshot
Title: Re: TChart start position
Post by: flori on February 28, 2021, 09:53:04 am
I would make the labels plain consecutive integer value: 0,1,2,3...etc

Then I create an array to map those values to the year:
Code: Pascal  [Select][+][-]
  1. type
  2.   TDynIntArray = array of Integer;
  3.  
  4. constructor TForm1.Create(AOwner: TComponent);
  5.  
  6.   procedure AAdd( var AArray: TDynIntArray ; Value:Integer );
  7.   var
  8.     i : Integer;
  9.   begin
  10.     SetLength(  AArray,Length(AArray) + 1 );
  11.     i := Length(AArray);
  12.     AArray[i-1] := Value;
  13.   end;
  14.  
  15. begin
  16.   inherited Create(AOwner);
  17.   AAdd(Self.FMapXYear,2012);
  18.   AAdd(Self.FMapXYear,2013);
  19.   AAdd(Self.FMapXYear,2019);
  20.   AAdd(Self.FMapXYear,2021);
  21. end;
  22.  

After that, I set the bottom TChartAxis onMarkToText event to print the year instead of the x value:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Chart1AxisList1MarkToText(var AText: String; AMark: Double);
  2. var
  3.   i : Integer;
  4. begin
  5.   i := StrToInt( AText);
  6.   if i >= 0 then begin
  7.     AText := IntToStr(Self.FMapXYear[i]);
  8.   end;
  9. end;
  10.  

Attached is the screenshot

Thanks. But How can I solve this problem when I use dbf? I attached small demo. Because the years change often. (1999, 2005, 2009, etc)
Title: Re: TChart start position
Post by: flori on February 28, 2021, 10:03:46 am
unfortunately it doesn't work with DBF or other DB (ZQUery, etc)
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 11:05:51 am
Thanks. But How can I solve this problem when I use dbf? I attached small demo. Because the years change often. (1999, 2005, 2009, etc)

Unfortunately, I can't load your project because I don't have the report component that you used.

The idea is what you put into the data is a simple continous integer, then you control what is to be shown in onMarkToText event.
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 11:42:19 am
I succesfully managed to compile your sample project.

Here what I modified:
1. Add a field XLABEL to farm.dbf, fill the record with 1,2
2. Change the property FieldX of DBChartSource1 to XLabel
3. Create onMarkText event:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Chart2AxisList1MarkToText(var AText: String; AMark: Double);
  2. var
  3.   XVal:Integer;
  4. begin
  5.   XVal := StrToInt(AText);
  6.   Dbf1.Locate('XLABEL',XvAL,[]);
  7.   AText := Dbf1.FieldByName('ID').AsString;
  8. end;
  9.  

The modified project is attached
Title: Re: TChart start position
Post by: flori on February 28, 2021, 12:10:43 pm
I succesfully managed to compile your sample project.

Here what I modified:
1. Add a field XLABEL to farm.dbf, fill the record with 1,2
2. Change the property FieldX of DBChartSource1 to XLabel
3. Create onMarkText event:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Chart2AxisList1MarkToText(var AText: String; AMark: Double);
  2. var
  3.   XVal:Integer;
  4. begin
  5.   XVal := StrToInt(AText);
  6.   Dbf1.Locate('XLABEL',XvAL,[]);
  7.   AText := Dbf1.FieldByName('ID').AsString;
  8. end;
  9.  

The modified project is attached

Thank you. It works with just 2 rows. While I add to 3 rows doesn't work.  (ID(2012)...+ID (i))
Title: Re: TChart start position
Post by: wp on February 28, 2021, 12:15:12 pm
See the attached modified demo with the dbf file.

The problem is that you essentially need two x values - one for the labels (years), and one for plotting, and the latter ones must be equidistant. If you'd use TAChart of Lazarus trunk you'd have the possibility to use multiple x values. But in the release version - which I am assuming in the demo project - this is not possible. However, you can extend the Text field of the data point record by the year values:
Code: Pascal  [Select][+][-]
  1.   Chart1ListSource1.Item[datapoint_index]^.Text := Format('%s|%g', [
  2.     Chart1ListSource1.Item[datapoint_index]^.Text,
  3.     Chart1ListSource1.Item[datapoint_index]^.X ]);
As you can see this adds the x value as a string the the original farm name, separated by a pipe character ('|').

Then for drawing the axis labels you must extract the year string from the combined Text field by finding the separator character and copying the text afterwards. This happens in the OnGetMarkText event of the x axis (or the OnMarkToText event)

The same is needed for the series labels - if you want to display them - by picking the text before the pipe character. This is the responsibility of the OnGerMark event of the series.

Since your data are not entered in an ordered fashion you must sort them. You can either sort the data set (which is preferred because it will also sort the DBGrid), or you can sort the ListChartSource (ListChartSource.Sorted := true).

When the data is sorted you iterate through the dataset and populate the ListSource. I avoid the DBChartSource at all because it turned out that it does not report the RecordCount correctly - which would show many empty bars in the chart...

After (or during) population of the ListChartSource the Text field is extended as explained above, and the X field is set to the running index to make it equidistant.

I think I should note that it is important to enclose the population steps of the ListChartSource by a BeginUpdate/EndUpdate block because otherwise the extent of the axis wil not be up-to-date.

I hope this description is clear enough so that you understand the principle. Study the code provided. Otherwise ask again.
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 12:20:33 pm
Thank you. It works with just 2 rows. While I add to 3 rows doesn't work.  (ID(2012)...+ID (i))

You have to manually set the content of the field XLABEL for every record. Modify the grid to also shows the XLABEL field....
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 12:21:50 pm
and make sure the records are sorted by XLABEL....
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 12:29:38 pm
Here is the modified version:
Title: Re: TChart start position
Post by: flori on February 28, 2021, 01:09:30 pm
WP and bpranoto thank you for your help

I am trying to implement the codes in FastReport (tfrxReport).

MANY THANK YOU!
Title: Re: TChart start position
Post by: bpranoto on February 28, 2021, 01:30:55 pm
You're welcome!
TinyPortal © 2005-2018