Recent

Author Topic: TAChart Bottom axis marks issue  (Read 4425 times)

Чебурашка

  • Hero Member
  • *****
  • Posts: 588
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
TAChart Bottom axis marks issue
« on: December 22, 2021, 09:09:20 am »
Hello,
I have a program that shows a bar series representing values on Y and days on X.
I am using on X the recommended TDateTimeIntervalChartSource.

Configuration of TDateTimeIntervalChartSource object:

Code: Pascal  [Select][+][-]
  1.   // frament taken from .lfm
  2.  
  3.   object DateTimeIntervalChartSource: TDateTimeIntervalChartSource
  4.     Params.MaxLength = 1
  5.     Params.MinLength = 1
  6.     Params.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps]
  7.     Params.Tolerance = 1
  8.     Steps = [dtsDay]
  9.     SuppressPrevUnit = False
  10.   end
  11.  

The call that updates the contents is:

Code: Pascal  [Select][+][-]
  1.     // int value corresponding to start day - 1
  2.     Chart.BottomAxis.Range.Min := fLastStartTime - 1; Chart.BottomAxis.Range.UseMin := True;
  3.  
  4.     // int value corresponding to start day + 1
  5.     Chart.BottomAxis.Range.Max := fLastEndTime + 1; Chart.BottomAxis.Range.UseMax := True;
  6.  
  7.     // Adjust Count in order to match when day ranges are longer (not clear why but seems to work fine both with Week view, month view and also longer intervals)
  8.     DateTimeIntervalChartSource.Params.Count :=
  9.     Math.Min(
  10.       System.Trunc(2.0 + System.Trunc((Chart.BottomAxis.Range.Max - Chart.BottomAxis.Range.Min) / 2.0) * 2.0 - 1)
  11.       , 41);
  12.  
  13.     UserDefinedChartSourceActualWeights.BeginUpdate();
  14.     UserDefinedChartSourceActualWeights.Reset();
  15.     UserDefinedChartSourceActualWeights.PointsNumber := fHistory.Count - 1;
  16.     UserDefinedChartSourceActualWeights.EndUpdate();
  17.  

Please see the following screenshots:

All except Capture-Month-Zoom-12-24 (=query over one month but zoomed on day 12 to 24) show the bottom axis well.
While I would like the bottom axis to be always visible regardless of the zoom level.

Any clue?

FULL SOURCE AND LFM

Code: Pascal  [Select][+][-]
  1. unit UFrameCyclesDailyWeightsHistory;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, ExtCtrls, TAGraph, TASeries, TASources, TACustomSource, TAIntervalSources, TATransformations,
  9.   TATools, TAChartAxisUtils;
  10.  
  11. type
  12.  
  13.   { TFrameCyclesDailyWeightsHistory }
  14.  
  15.   TFrameCyclesDailyWeightsHistory = class(TFrame)
  16.     Chart: TChart;
  17.     ChartAxisWeightTransformationsLeft: TChartAxisTransformations;
  18.     ChartAxisWeightTransformationsLeftAutoScaleAxisTransform: TAutoScaleAxisTransform;
  19.  
  20.     ChartBarSeriesWeights: TBarSeries;
  21.     DateTimeIntervalChartSource: TDateTimeIntervalChartSource;
  22.     PanelChartControls: TPanel;
  23.     PanelMain: TPanel;
  24.     UserDefinedChartSourceActualWeights: TUserDefinedChartSource;
  25.     procedure ChartAxisList1MarkToText(var AText: String; AMark: Double);
  26.     procedure UserDefinedChartSourceGetChartDataItem(ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
  27.   private
  28.     fLastStartTime, fLastEndTime: TDateTime;
  29.     fHistory: TList;
  30.     fAverage: Pointer;
  31.   public
  32.     procedure SetSource(lastStartTime: TDateTime; lastEndTime: TDateTime; history: TList; average: Pointer);
  33.     procedure Translate();
  34.     procedure ShowContent();
  35.     constructor Create(TheOwner: TComponent); override;
  36.   end;
  37.  
  38. implementation
  39.  
  40. {$R *.lfm}
  41.  
  42. uses
  43.   Graphics
  44.   , DateUtils
  45.   , Math
  46.  
  47.   ,ProdLogQueries
  48.   ;
  49.  
  50. ////////////////////////////////////////////////////////////////////////////////
  51. // TFrameCyclesDailyWeightsHistory
  52. ////////////////////////////////////////////////////////////////////////////////
  53.  
  54. constructor TFrameCyclesDailyWeightsHistory.Create(TheOwner: TComponent);
  55. begin
  56.   inherited Create(TheOwner);
  57.  
  58.   fHistory := nil;
  59.   fAverage := nil;
  60. end;
  61.  
  62. procedure TFrameCyclesDailyWeightsHistory.UserDefinedChartSourceGetChartDataItem(ASource: TUserDefinedChartSource; AIndex: Integer;
  63.   var AItem: TChartDataItem);
  64. var
  65.   weightT: Single;
  66. begin
  67.   if (fHistory <> nil)
  68.      and (fHistory.Count > 1)
  69.      and (0 <= AIndex)
  70.      and (AIndex < fHistory.Count - 1)
  71.      then
  72.   begin
  73.     AItem.X :=  PPoweronCycle(fHistory[AIndex + 1])^.ReferenceTime;
  74.  
  75.     if (ASource = UserDefinedChartSourceActualWeights) then
  76.     begin
  77.       weightT := PPoweronCycle(fHistory[AIndex + 1])^.ActualWeight / 1000;
  78.       AItem.Y := weightT;
  79.       AItem.Text := Format('%0.2f t', [weightT]);
  80.       Exit;
  81.     end;
  82.   end;
  83. end;
  84.  
  85. procedure TFrameCyclesDailyWeightsHistory.ChartAxisList1MarkToText(var AText: String; AMark: Double);
  86. begin
  87.   AText := SysUtils.FormatDateTime('mmm', AMark) + System.LineEnding + SysUtils.FormatDateTime('dd', AMark);
  88. end;
  89.  
  90. procedure TFrameCyclesDailyWeightsHistory.SetSource(lastStartTime: TDateTime; lastEndTime: TDateTime; history: TList; average: Pointer);
  91. begin
  92.   fLastStartTime := lastStartTime;
  93.   fLastEndTime := lastEndTime;
  94.   fHistory := history;
  95.   fAverage := average;
  96. end;
  97.  
  98. ////////////////////////////////////////////////////////////////////////////////
  99. // Translate/ShowContent
  100. ////////////////////////////////////////////////////////////////////////////////
  101.  
  102. procedure TFrameCyclesDailyWeightsHistory.Translate();
  103. begin
  104.  
  105. end;
  106.  
  107. procedure TFrameCyclesDailyWeightsHistory.ShowContent();
  108. begin
  109.   if (fHistory <> nil)
  110.      and (fHistory.Count > 0) then
  111.   begin
  112.     Chart.BottomAxis.Range.Min := fLastStartTime - 1;
  113.     Chart.BottomAxis.Range.UseMin := True;
  114.     Chart.BottomAxis.Range.Max := fLastEndTime + 1;
  115.     Chart.BottomAxis.Range.UseMax := True;
  116.     DateTimeIntervalChartSource.Params.Count :=
  117.     Math.Min(
  118.       System.Trunc(2.0 + System.Trunc((Chart.BottomAxis.Range.Max - Chart.BottomAxis.Range.Min) / 2.0) * 2.0 - 1)
  119.       , 41);
  120.  
  121.     UserDefinedChartSourceActualWeights.BeginUpdate();
  122.     UserDefinedChartSourceActualWeights.Reset();
  123.     UserDefinedChartSourceActualWeights.PointsNumber := fHistory.Count - 1;
  124.     UserDefinedChartSourceActualWeights.EndUpdate();
  125.   end;
  126. end;
  127.  
  128.  
  129. end.
  130.  

Code: Pascal  [Select][+][-]
  1.  
  2. object FrameCyclesDailyWeightsHistory: TFrameCyclesDailyWeightsHistory
  3.   Left = 0
  4.   Height = 489
  5.   Top = 0
  6.   Width = 1383
  7.   ClientHeight = 489
  8.   ClientWidth = 1383
  9.   TabOrder = 0
  10.   DesignLeft = 357
  11.   DesignTop = 112
  12.   object PanelMain: TPanel
  13.     Left = 2
  14.     Height = 435
  15.     Top = 52
  16.     Width = 1379
  17.     Align = alClient
  18.     BorderSpacing.Around = 2
  19.     BevelOuter = bvNone
  20.     ClientHeight = 435
  21.     ClientWidth = 1379
  22.     TabOrder = 0
  23.     object Chart: TChart
  24.       Left = 0
  25.       Height = 435
  26.       Top = 0
  27.       Width = 1379
  28.       AxisList = <      
  29.         item
  30.           Marks.LabelBrush.Style = bsClear
  31.           Minors = <>
  32.           Range.Max = 15
  33.           Range.UseMax = True
  34.           Range.UseMin = True
  35.           Title.LabelFont.Orientation = 900
  36.           Title.Visible = True
  37.           Title.Caption = 'Weight [t]'
  38.           Title.LabelBrush.Style = bsClear
  39.         end      
  40.         item
  41.           Alignment = calBottom
  42.           Marks.Alignment = taCenter
  43.           Marks.Format = '%2:s'
  44.           Marks.LabelBrush.Style = bsClear
  45.           Marks.OverlapPolicy = opHideNeighbour
  46.           Marks.Source = DateTimeIntervalChartSource
  47.           Marks.Style = smsLabel
  48.           Minors = <>
  49.           Title.Visible = True
  50.           Title.Caption = 'Days'
  51.           Title.LabelBrush.Style = bsClear
  52.           OnMarkToText = ChartAxisList1MarkToText
  53.         end>
  54.       Foot.Brush.Color = clBtnFace
  55.       Foot.Font.Color = clBlue
  56.       Legend.Alignment = laTopCenter
  57.       Legend.ColumnCount = 3
  58.       Title.Brush.Color = clBtnFace
  59.       Title.Font.Color = clBlue
  60.       Title.Text.Strings = (
  61.         'TAChart'
  62.       )
  63.       Align = alClient
  64.       object ChartBarSeriesWeights: TBarSeries
  65.         Marks.Distance = 10
  66.         Marks.LabelFont.Orientation = 900
  67.         Marks.Format = '%2:s'
  68.         Marks.Style = smsLabel
  69.         Title = 'Weight'
  70.         AxisIndexX = 1
  71.         AxisIndexY = 0
  72.         BarBrush.Color = clBlack
  73.         BarWidthPercent = 80
  74.         Source = UserDefinedChartSourceActualWeights
  75.         Stacked = False
  76.       end
  77.     end
  78.   end
  79.   object PanelChartControls: TPanel
  80.     Left = 0
  81.     Height = 50
  82.     Top = 0
  83.     Width = 1383
  84.     Align = alTop
  85.     BevelOuter = bvNone
  86.     TabOrder = 1
  87.   end
  88.   object UserDefinedChartSourceActualWeights: TUserDefinedChartSource
  89.     OnGetChartDataItem = UserDefinedChartSourceGetChartDataItem
  90.     left = 352
  91.     top = 304
  92.   end
  93.   object DateTimeIntervalChartSource: TDateTimeIntervalChartSource
  94.     Params.MaxLength = 1
  95.     Params.MinLength = 1
  96.     Params.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps]
  97.     Params.Tolerance = 1
  98.     Steps = [dtsDay]
  99.     SuppressPrevUnit = False
  100.     left = 247
  101.     top = 35
  102.   end
  103. end
  104.  


« Last Edit: December 22, 2021, 09:29:04 am by tt »
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

wp

  • Hero Member
  • *****
  • Posts: 12909
Re: TAChart Bottom axis marks issue
« Reply #1 on: December 22, 2021, 11:04:54 am »
The Steps property of the TDateTimeIntervalChartSource is often misunderstood. It does not mean that you want to see labels at the Steps intervals, but it means that labels are displayed only when the source advances labels in the units selected in Steps. When you allow only [dtDay] steps labels will only appear when the source is advancing the labels in day units. When you zoom into to dataset a lot the source may switch down to hour or even minute steps - but they will not be displayed because you allowed only labels for day steps.

It is a good idea to not touch the Steps list for this reason. Having dtHours in the list again, however, the labels in the zoomed state may be written in the "dd hh:nn" format - but you wanted dates only. You could edit the HourFormat in the DateTimeStepFormat to become "dd mm" - but then you will see the same label several times and at unexpected positions (where the times used to appear). So - quite a mess...

The TDateTimeIntervalChartSource is great to display all-purpose date/time axis labels. But it quickly becomes uncomfortable when there are special requirements. In such cases I'd recommend to replace the DateTimeIntervalChartSource by a dedicated ListChartSource which you populate with the date/time values where you need labels to appear. Write a short method which populates the source for you in full-day steps:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.UpdateDateLabels;
  2. var
  3.   ext: TDoubleRect;
  4.   d: TDate;
  5. begin
  6.   ext := Chart.LogicalExtent;
  7.   DateLabelsSource.Clear;  // DateLabelsSource is a TListChartSource added to the form and attached to BottomAxis.Marks.Source
  8.   d := trunc(ext.a.x);
  9.   repeat
  10.     DateLabelsSource.Add(d, d, FormatDateTime('dd'+LineEnding+'mmm', d));
  11.     d := d + 1.0;
  12.   until d > ext.b.x;
  13. end;

Call this in the handler for the OnExtentChanged event - this fires whenever the axis range changes, for example when new data values are added, when you zoom or pan the visible viewport, or similar:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ChartExtentChanged(ASender: TChart);
  2. begin
  3.   UpdateDateLabels;
  4. end;
  5.  

Чебурашка

  • Hero Member
  • *****
  • Posts: 588
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: TAChart Bottom axis marks issue
« Reply #2 on: December 23, 2021, 09:21:25 am »
Thanks for the kind explanation and code suggestion.

Let me say anyway that the TAChart package is a very very very good piece of software. It is powerful and well written. Contratulations to authors and contributors.
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

 

TinyPortal © 2005-2018