Recent

Author Topic: Plotting Scatter & Bubble Charts From Text File  (Read 778 times)

sdawilson

  • New Member
  • *
  • Posts: 14
Plotting Scatter & Bubble Charts From Text File
« on: May 27, 2024, 10:19:50 am »
Morning all,

Firstly, thank you to wp for your support. I tried investigating your points this morning and I'm still lost.

This is driving me mad! I'm so close (see attached screen shot), I know it must be simple, but I just can't fix two things.

Background:
I'm writing an app to support a research project tracking carbon in infrastructure projects. The app reads a text file (output by MS Access, see attached) and plots either a bubble chart or a scatter chart and outputs the chart to an image file, which is then displayed in an Access image control. This plugs a huge hole in Access after the removal of Pivot Charts in Access 2013. The app has two command line arguments: the name of the text file containing the chart parameters and chart data, and the "/s" (silent) switch for "run and quit".

My first attempt was to use Excel as an embedded OLE object. Robust, pretty, but horribly, horribly slow... 26 - 28 seconds. I then wrote an app in Python using Matplotlib which worked perfectly, but it took 15 seconds to load and run. It was also a nightmare to distribute to the working group (42 local authorities in the UK). The Lazarus compiled app is elegant (as it creates a single, small distributable) and is very fast (as it runs and returns the chart image in under 2 seconds).

Problem
I posted my two problems last night (see attached screenshot):

1. The axes don't hold to the specified ranges, even where the datapoints are within those ranges (see attached jpeg).
2. The legend colour for scatter charts doesn't align with the colour on the chart.

These problems only relate to scatter charts (I think).

Confession
I've been coding in Pascal / Lazarus for a week, so I'm a super-newbie. My skills are in VB, VB.NET, and VBA (mostly in Excel and Project), so some of the principles are very familiar, others not-so-much. My limited Pascal skills are reflected in my coding style.

Code
I'll post my application code below and if anyone could help fix the issues, this would really help my research project.

Thank you for any support you could give me.
« Last Edit: May 27, 2024, 10:21:47 am by sdawilson »

sdawilson

  • New Member
  • *
  • Posts: 14
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #1 on: May 27, 2024, 10:20:23 am »
Here's the lfm:

Code: Pascal  [Select][+][-]
  1. object Form1: TForm1
  2.   Left = 963
  3.   Height = 1722
  4.   Top = 262
  5.   Width = 1719
  6.   Caption = 'Chart Script'
  7.   ClientHeight = 1722
  8.   ClientWidth = 1719
  9.   DesignTimePPI = 240
  10.   OnCreate = FormCreate
  11.   OnDestroy = FormDestroy
  12.   LCLVersion = '3.2.0.0'
  13.   object tabDisplay: TPageControl
  14.     Left = 16
  15.     Height = 1608
  16.     Top = 8
  17.     Width = 1690
  18.     ActivePage = tabChart
  19.     Anchors = [akTop, akLeft, akRight, akBottom]
  20.     TabIndex = 1
  21.     TabOrder = 0
  22.     object tabScript: TTabSheet
  23.       Caption = 'Script'
  24.       ClientHeight = 1536
  25.       ClientWidth = 1670
  26.       object grdScript: TStringGrid
  27.         Left = 8
  28.         Height = 1400
  29.         Top = 128
  30.         Width = 1650
  31.         Anchors = [akTop, akLeft, akRight, akBottom]
  32.         ColCount = 3
  33.         ExtendedSelect = False
  34.         Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goSmoothScroll]
  35.         RowCount = 1
  36.         TabOrder = 0
  37.         ColWidths = (
  38.           389
  39.           357
  40.           584
  41.         )
  42.         Cells = (
  43.           3
  44.           0
  45.           0
  46.           'Section'
  47.           1
  48.           0
  49.           'Property'
  50.           2
  51.           0
  52.           'Setting'
  53.         )
  54.       end
  55.       object cmbFiles: TComboBox
  56.         Left = 8
  57.         Height = 49
  58.         Top = 64
  59.         Width = 1650
  60.         Anchors = [akTop, akLeft, akRight]
  61.         ItemHeight = 41
  62.         TabOrder = 1
  63.         OnClick = cmbFilesClick
  64.       end
  65.       object Label1: TLabel
  66.         Left = 8
  67.         Height = 41
  68.         Top = 16
  69.         Width = 132
  70.         Caption = 'File Name'
  71.       end
  72.     end
  73.     object tabChart: TTabSheet
  74.       Caption = 'Chart'
  75.       ClientHeight = 1536
  76.       ClientWidth = 1670
  77.       object chtPlot: TChart
  78.         Left = 8
  79.         Height = 1520
  80.         Top = 8
  81.         Width = 1658
  82.         AxisList = <        
  83.           item
  84.             Intervals.Count = 4
  85.             Intervals.MaxLength = 0
  86.             Intervals.MinLength = 0
  87.             Intervals.Options = [aipUseCount]
  88.             Marks.Format = '%0:.0f'
  89.             Marks.LabelBrush.Style = bsClear
  90.             Marks.Range.Max = 100
  91.             Marks.Range.UseMax = True
  92.             Marks.Range.UseMin = True
  93.             Marks.Style = smsCustom
  94.             Minors = <>
  95.             Range.Max = 100
  96.             Range.UseMax = True
  97.             Range.UseMin = True
  98.             Title.LabelFont.Orientation = 900
  99.             Title.Visible = True
  100.             Title.Caption = 'Y Axis'
  101.             Title.LabelBrush.Style = bsClear
  102.           end        
  103.           item
  104.             Intervals.Count = 10
  105.             Intervals.MaxLength = 0
  106.             Intervals.MinLength = 0
  107.             Intervals.Options = [aipUseCount]
  108.             Alignment = calBottom
  109.             Marks.Format = '%0:.0f'
  110.             Marks.LabelBrush.Style = bsClear
  111.             Marks.Range.Max = 100
  112.             Marks.Range.UseMax = True
  113.             Marks.Range.UseMin = True
  114.             Marks.Style = smsCustom
  115.             Minors = <>
  116.             Range.Max = 100
  117.             Range.UseMax = True
  118.             Range.UseMin = True
  119.             Title.Visible = True
  120.             Title.Caption = 'X Axis'
  121.             Title.LabelBrush.Style = bsClear
  122.           end>
  123.         BackColor = clCream
  124.         Extent.UseXMax = True
  125.         Extent.UseXMin = True
  126.         Extent.UseYMax = True
  127.         Extent.UseYMin = True
  128.         Extent.XMax = 100
  129.         Extent.YMax = 100
  130.         ExtentSizeLimit.UseXMax = True
  131.         ExtentSizeLimit.UseXMin = True
  132.         ExtentSizeLimit.UseYMax = True
  133.         ExtentSizeLimit.UseYMin = True
  134.         ExtentSizeLimit.XMax = 100
  135.         ExtentSizeLimit.YMax = 100
  136.         Legend.Visible = True
  137.         Title.Font.CharSet = ANSI_CHARSET
  138.         Title.Font.Height = -40
  139.         Title.Font.Pitch = fpVariable
  140.         Title.Font.Quality = fqDraft
  141.         Title.Font.Style = [fsBold]
  142.         Title.Text.Strings = (
  143.           'Chart'
  144.         )
  145.         Title.Visible = True
  146.         OnBeforeDrawBackWall = chtPlotBeforeDrawBackWall
  147.         Anchors = [akTop, akLeft, akRight, akBottom]
  148.         Color = clWhite
  149.       end
  150.     end
  151.   end
  152.   object cmdClose: TButton
  153.     Left = 1518
  154.     Height = 80
  155.     Top = 1624
  156.     Width = 188
  157.     Anchors = [akRight, akBottom]
  158.     Caption = 'Close'
  159.     Color = 15912605
  160.     Font.Style = [fsBold]
  161.     ParentFont = False
  162.     TabOrder = 1
  163.     OnClick = cmdCloseClick
  164.   end
  165.   object cmdUpdate: TButton
  166.     Left = 18
  167.     Height = 80
  168.     Top = 1624
  169.     Width = 188
  170.     Anchors = [akLeft, akBottom]
  171.     Caption = 'Update'
  172.     Color = 15912605
  173.     Font.Style = [fsBold]
  174.     ParentFont = False
  175.     TabOrder = 2
  176.     OnClick = cmdUpdateClick
  177.   end
  178. end

Here's my code:

Code: Pascal  [Select][+][-]
  1. unit ReadScriptFile;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   LazUtils, StrUtils, FileUtil, TAGraph, TASeries, TAStyles, TATools, TAMultiSeries, Classes,
  9.   SysUtils, Forms, Controls, Graphics, Dialogs, Grids, StdCtrls, ComCtrls, ValEdit, TACustomSeries, TASources,
  10.   Types, TATypes, TACustomSource, TADrawUtils, TAChartUtils, TAChartAxisUtils;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     chtPlot: TChart;
  18.     cmdClose: TButton;
  19.     cmdUpdate: TButton;
  20.     cmbFiles: TComboBox;
  21.     Label1: TLabel;
  22.     grdScript: TStringGrid;
  23.     tabDisplay: TPageControl;
  24.     tabScript: TTabSheet;
  25.     tabChart: TTabSheet;
  26.     procedure chtPlotBeforeDrawBackWall(ASender: TChart; ACanvas: TCanvas;
  27.       const ARect: TRect; var ADoDefaultDrawing: Boolean);
  28.     procedure cmbFilesClick(Sender: TObject);
  29.     procedure cmdCloseClick(Sender: TObject);
  30.     procedure cmdUpdateClick(Sender: TObject);
  31.     procedure FormCreate(Sender: TObject);
  32.     procedure FormDestroy(Sender: TObject);
  33.     procedure LoadFileBox(aFName: String);
  34.     procedure SetAxisAttributes();
  35.     procedure LoadScript();
  36.     procedure ProcessScript();
  37.     procedure PlotSeries();
  38.     function ColorStrToColor(WebColor: string): TColor;
  39.     function RGB(r, g, b: Byte): TColor;
  40.   private
  41.     FBackImage: TPicture;
  42.   public
  43.  
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1; sChartTYpe: string; pPointerSize: integer;
  48.   pMinX: double; pMaxX: double; pMinY: double; pMaxY: double; pStopsX: integer; pStopsY: integer;
  49.  
  50.  
  51. implementation
  52.  
  53. {$R *.lfm}
  54.  
  55. { TForm1 }
  56.  
  57. procedure TForm1.FormCreate(Sender: TObject);
  58. var
  59.    pParam: string;
  60.    pFile: string;
  61. begin
  62.   LoadFileBox('D:\Lazarus');
  63.   If paramcount > 0 then
  64.      cmbFiles.Text :=  paramstr(1);
  65.      LoadScript;
  66.      ProcessScript;
  67.      If paramcount =2 then
  68.         if lowercase(paramstr(2)) = '/s' then
  69.            begin
  70.            beep;
  71.            close;
  72.            halt(0);
  73.            end
  74. end;
  75.  
  76. procedure TForm1.FormDestroy(Sender: TObject);
  77. begin
  78.   FBackImage.Free;
  79. end;
  80.  
  81. procedure TForm1.chtPlotBeforeDrawBackWall(ASender: TChart; ACanvas: TCanvas;
  82.   const ARect: TRect; var ADoDefaultDrawing: Boolean);
  83. begin
  84.   ACanvas.StretchDraw(ARect, FBackImage.Graphic);
  85.   ADoDefaultDrawing := false;
  86. end;
  87.  
  88. procedure TForm1.cmbFilesClick(Sender: TObject);
  89. begin
  90.   LoadScript;
  91. end;
  92.  
  93. procedure TForm1.cmdCloseClick(Sender: TObject);
  94.   var
  95.   pSelected: Integer;
  96. begin
  97.   // Show a confirmation dialog
  98.   pSelected := MessageDlg('Are you sure?',mtConfirmation, mbOKCancel, 0);
  99.  
  100.   // Show the button type selected
  101.   if pSelected = mrOK then Close;
  102.  
  103. end;
  104.  
  105. procedure TForm1.cmdUpdateClick(Sender: TObject);
  106. begin
  107.   ProcessScript;
  108. end;
  109.  
  110. procedure TForm1.LoadFileBox(aFName: String);
  111. var
  112.   list: TStringList;
  113. begin
  114.   list := FindAllFiles(aFName, '*.txt' , False {don't search in subdirectory});
  115.   cmbFiles.Items := list;
  116.   list.Free;
  117. end;
  118.  
  119. procedure Tform1.LoadScript();
  120.  
  121. var
  122.   pFile: TextFile;
  123.   pPath: string;
  124.   pLine: string;
  125.   pSection: string;
  126.   pFunction: TStringArray;
  127.   pRow: integer;
  128. begin
  129.   while grdScript.RowCount > 1 do
  130.   begin
  131.     grdScript.DeleteRow(1);
  132.   end;
  133.   //showmessage(pPath);
  134.   pPath:= cmbFiles.Text;
  135.   AssignFile(pFile, pPath);
  136.    try
  137.     // Open the file for reading
  138.      reset(pFile);
  139.     // Keep reading lines until the end of the file is reached
  140.     //lstscript.Items.clear;
  141.     while not eof(pFile) do
  142.     begin
  143.       readln(pFile, pLine);
  144.       if Trim(pLine) <> '' then
  145.         begin
  146.         if pos(':',pLine)=0 then
  147.            begin
  148.              pSection := pLine;
  149.             // pLine := pSection + #9 + '' + #9 + '';
  150.             // lstScript.Items.Add(pLine);
  151.            end;
  152.         if pos(':',pLine)>0 then
  153.            begin
  154.              pFunction := pLine.Split(':');
  155.              pRow := grdScript.RowCount;
  156.              grdScript.RowCount := pRow + 1;
  157.              grdScript.Cells[0, pRow] := trim(pSection);
  158.              grdScript.Cells[1, pRow] := trim(pFunction[0]);
  159.              grdScript.Cells[2, pRow] := trim(pFunction[1]);
  160.              if high(pFunction)>1 then
  161.                grdScript.Cells[2, pRow] := trim(pFunction[1]) + ':' + trim(pFunction[2]);
  162.            end;
  163.         end;
  164.           // lstScript.Items.Add(pLine);
  165.     end;
  166.      // Done so close the file
  167.      CloseFile(pFile);
  168.    except
  169.     on E: EInOutError do
  170.     ShowMessage('File not found.');
  171.   end;
  172. end;
  173.  
  174. procedure Tform1.ProcessScript();
  175. var
  176.  pRow: integer;
  177.  pRowCount: integer;
  178.  pCol: integer;
  179.  pSection: string;
  180.  pProperty: string;
  181.  pSetting: string;
  182.  pAxis: integer;
  183.  pFile: string;
  184.  pFormat: string;
  185. begin
  186.   pRowCount := grdScript.RowCount - 1;
  187.   for pRow := 1 to pRowCount do
  188.   begin
  189.     pSection := grdScript.Cells[0, pRow];
  190.     pProperty := grdScript.Cells[1, pRow];
  191.     pSetting := grdScript.Cells[2, pRow];
  192.  
  193.     //Chart...
  194.     If lowercase(pSection) = 'chart' then
  195.     begin
  196.       If LowerCase(pProperty) = 'type' then sChartType := lowercase(pSetting);
  197.       if LowerCase(pProperty) = 'save' then pFile := pSetting;
  198.       if LowerCase(pProperty) = 'format' then pFormat := lowercase(pSetting);
  199.       if LowerCase(pProperty) = 'pointer size' then pPointerSize := lowercase(pSetting).ToInteger;
  200.       if LowerCase(pProperty) = 'background' then
  201.         begin
  202.             FBackImage := TPicture.Create;
  203.             FBackImage.LoadFromFile(pSetting);
  204.         end;
  205.     end;
  206.  
  207.     //Title...
  208.     If lowercase(pSection) = 'title' then
  209.     begin
  210.       if lowercase(pProperty) = 'text' then chtplot.Title.Text.Text := pSetting;
  211.       if lowercase(pProperty) = 'size' then chtplot.Title.Font.Size := pSetting.ToInteger;
  212.       if lowercase(pProperty) = 'bold' then
  213.         If lowercase(pSetting) = 'true' then
  214.           chtplot.Title.Font.Bold := true
  215.         else
  216.           chtplot.Title.Font.Bold := false;
  217.       if LowerCase(pProperty) = 'color' then
  218.         begin
  219.           pSetting := UpperCase(pSetting);
  220.           chtPlot.Title.Font.Color := ColorStrToColor(pSetting);
  221.         end;
  222.     end;
  223.  
  224.     //Set Axis...
  225.     If Pos('axis',lowercase(pSection)) > 0 then
  226.       case lowercase(pSection) of
  227.         'xaxis': pAxis := 1;
  228.         'yaxis': pAxis := 0;
  229.       end
  230.     else
  231.       pAxis := -1;
  232.  
  233.     If pAxis >= 0 then
  234.     begin
  235.       chtPlot.Axislist[pAxis].Title.Distance := chtplot.Title.Font.Size + 30;
  236.       if LowerCase(pProperty) = 'text' then chtPlot.Axislist[pAxis].Title.Caption := pSetting;
  237.       if LowerCase(pProperty) = 'size' then chtPlot.Axislist[pAxis].LabelSize := pSetting.ToInteger;
  238.       if LowerCase(pProperty) = 'color' then
  239.         begin
  240.           pSetting := UpperCase(pSetting);
  241.           chtPlot.AxisList[pAxis].Title.LabelFont.Color := ColorStrToColor(pSetting);
  242.         end;
  243.  
  244.       if LowerCase(pProperty) = 'min' then
  245.         If pAxis = 0 then pMinY := pSetting.ToInteger else pMinX :=pSetting.ToInteger;
  246.  
  247.       if lowercase(pProperty) = 'max' then
  248.           If pAxis = 0 then pMaxY := pSetting.ToInteger else pMaxX :=pSetting.ToInteger;
  249.  
  250.       if LowerCase(pProperty) = 'stops' then
  251.           If pAxis = 0 then pStopsY := pSetting.ToInteger else pStopsX :=pSetting.ToInteger;
  252.  
  253.       if LowerCase(pProperty) = 'margin' then chtPlot.Axislist[pAxis].Title.Distance := chtplot.Title.Font.Size + psetting.ToInteger ;
  254.  
  255.       if LowerCase(pProperty) = 'bold' then
  256.         If lowercase(pSetting) = 'true' then
  257.           chtPlot.Axislist[pAxis].Title.LabelFont.Bold := true
  258.         else
  259.           chtPlot.Axislist[pAxis].Title.LabelFont.Bold := false;
  260.     end;
  261.   end;
  262.  
  263.   //Plot data...
  264.  
  265.   PlotSeries;
  266.   SetAxisAttributes;
  267.  
  268.   //Save graphic file.
  269.   if pFile<>'' then
  270.     if (pFormat = 'jpeg') or (pFormat = 'jpg') then
  271.       chtPlot.SaveToFile(TJPEGImage, pFile);
  272.     if (pformat = 'bmp') or (pformat = 'bitmap') then
  273.       chtPlot.SaveToFile(TBitmap, pFile);
  274.     if (pformat = 'png') or (pformat = 'portable network graphic') then
  275.       chtPlot.SaveToFile(TPortableNetworkGraphic, pFile);
  276. end;
  277.  
  278. procedure Tform1.PlotSeries();
  279. var
  280.   pRow: integer;
  281.   pRowCount: integer;
  282.   pSeriesBP: TBubbleSeries;
  283.   pSeriesXY: TLineSeries;
  284.   pSeriesPtr: integer;
  285.   pSection: string;
  286.   pProperty: string;
  287.   pSetting: string;
  288.   pData: array of string;
  289.   pX: Double;
  290.   pY: Double;
  291.   pR: Double;
  292.   pPointer: string;
  293.   pShape: integer;
  294.   pColor: Tcolor;
  295. //  pSeriesMarks: TseriesMarksStyle;
  296. begin
  297.   pRowCount := grdScript.RowCount - 1;
  298.   pSeriesPtr :=1;
  299.   chtPlot.ClearSeries;
  300.   for pRow := 1 to pRowCount do
  301.   begin
  302.     pSection := LowerCase(grdScript.Cells[0, pRow]);
  303.     pProperty := grdScript.Cells[1, pRow];
  304.     pSetting := grdScript.Cells[2, pRow];
  305.  
  306.     //Bubble chart...
  307.     If pSection = 'bubble series' then
  308.       begin
  309.         pSeriesBP := TBubbleSeries.Create(chtPlot);
  310.         chtPlot.AddSeries(pSeriesBP);
  311.         pData := pSetting.Split(',');
  312.         pX := pData[0].ToDouble;
  313.         pY := pData[1].ToDouble;
  314.         pR := pData[2].ToDouble;
  315.         If lowercase(pData[3]) = 'random' then
  316.           pColor := (Random(256) * Random(256)) + Random(256)
  317.         else
  318.           pColor := ColorStrToColor(pData[3]);
  319.  
  320.         pSeriesBP.AddXY(pX, pY, pR,pProperty, pColor);
  321.         pSeriesBP.BubbleBrush.Color:= pColor;
  322.         pSeriesBP.Transparency:=60;
  323.         If lowercase(pData[4]) = 'data_labels=true' then
  324.           begin
  325.             pSeriesBP.Marks.Format:= pProperty + ',' + pData[0] + ',' + pData[1] + ',' + pData[2];
  326.             pSeriesBP.Marks.Visible := true;
  327.           end;
  328.         pSeriesBP.Name := pProperty;
  329.         pSeriesBP.Title := pProperty;
  330.         pSeriesBP.ShowInLegend:=true;
  331.       end;
  332.  
  333.     //Scatter series...
  334.     If pSection = 'scatter series' then
  335.       begin
  336.         pSeriesXY := TLineSeries.Create(chtPlot);
  337.         chtPlot.AddSeries(pSeriesXY);
  338.         pData := pSetting.Split(',');
  339.         pX := pData[0].ToDouble;
  340.         pY := pData[1].ToDouble;
  341.         pPointer := lowercase(pData[2]);
  342.  
  343.         //Process colour...
  344.         If lowercase(pData[3]) = 'random' then
  345.           pColor := (Random(256) * Random(256)) + Random(256)
  346.         else
  347.           pColor := ColorStrToColor(pData[3]);
  348.  
  349.  
  350.  
  351.         // Process point data...
  352.         pSeriesXY.AddXY(pX, pY, pProperty, pColor);
  353.         pSeriesXY.ShowLines:= false;
  354.         pSeriesXY.ShowPoints:=true;
  355.         pSeriesXY.LinePen.Color := pColor;
  356.         pSeriesXY.SeriesColor := pColor;
  357.  
  358.         If pPointerSize=0 then
  359.           pPointerSize := 15;
  360.  
  361.         pSeriesXY.Pointer.HorizSize:=pPointerSize;
  362.         pSeriesXY.Pointer.VertSize:=pPointerSize;
  363.  
  364.         // Process point shape...
  365.         case pPointer of
  366.           'cross': pSeriesXY.Pointer.Style := psCross;
  367.           'circle': pSeriesXY.Pointer.Style:= psCircle;
  368.           'diagcross': pSeriesXY.Pointer.Style := psDiagCross;
  369.           'downtriangle': pSeriesXY.Pointer.Style := psDownTriangle;
  370.           'triange': pSeriesXY.Pointer.Style := psTriangle;
  371.           'star': pSeriesXY.Pointer.Style := psStar;
  372.           'diamond': pSeriesXY.Pointer.Style := psDiamond;
  373.           'lefttriangle': pSeriesXY.Pointer.Style := psLeftTriangle;
  374.           'righttriangle': pSeriesXY.Pointer.Style := psRightTriangle;
  375.           'vertbar': pSeriesXY.Pointer.Style := psVertBar;
  376.           'horbar': pSeriesXY.Pointer.Style := psHorBar;
  377.           'point': pSeriesXY.Pointer.Style := psPoint;
  378.           'hexagon': pSeriesXY.Pointer.Style := psHexagon;
  379.           'fullstar': pSeriesXY.Pointer.Style := psFullStar;
  380.           'lowbracket': pSeriesXY.Pointer.Style := psLowBracket;
  381.           'highbracket': pSeriesXY.Pointer.Style := psHighBracket;
  382.           'leftbracket': pSeriesXY.Pointer.Style := psLeftBracket;
  383.           'rightbracket': pSeriesXY.Pointer.Style := psRightBracket;
  384.           'none': pSeriesXY.Pointer.Style := psNone;
  385.         else
  386.           pSeriesXY.Pointer.Style:= psCircle;
  387.         end;
  388.  
  389.         pSeriesXY.Transparency:=30;
  390.         If lowercase(pData[4]) = 'data_labels=true' then
  391.           begin
  392.             pSeriesXY.Marks.Format:= pProperty + ',' + pData[0] + ',' + pData[1];
  393.             pSeriesXY.Marks.Visible := true;
  394.           end;
  395.         pSeriesXY.Name := pProperty;
  396.         pSeriesXY.Title := pProperty;
  397.         pSeriesXY.ShowInLegend:=true;
  398.       end;
  399.     end;
  400. end;
  401.  
  402. procedure Tform1.SetAxisAttributes();
  403. begin
  404.  
  405.   // 'xaxis': pAxis := 1;
  406.   // 'yaxis': pAxis := 0;
  407.  
  408.   chtPlot.Axislist[0].Range.Min := pMinY;
  409.   chtPlot.AxisList[0].Range.Max := pMaxY;
  410.   chtPlot.Axislist[0].Marks.Range.Min := pMinY;
  411.   chtPlot.Axislist[0].Marks.Range.Max := pMaxY;
  412.   chtPlot.Axislist[0].Intervals.Count := pStopsY;
  413.  
  414.   chtPlot.Axislist[1].Range.Min := pMinX;
  415.   chtPlot.AxisList[1].Range.Max := pMaxX;
  416.   chtPlot.Axislist[1].Marks.Range.Min := pMinX;
  417.   chtPlot.Axislist[1].Marks.Range.Max := pMaxX;
  418.   chtPlot.Axislist[1].Intervals.Count := pStopsX;
  419.  
  420.   chtPlot.Extent.YMax := pMaxY;
  421.   chtPlot.Extent.YMin := pMinY;
  422.   chtPlot.Extent.XMax := pMaxX;
  423.   chtPlot.Extent.XMin := pMinX;
  424.   chtPlot.Extent.UseXMax := true;
  425.   chtPlot.Extent.UseXMin := true;
  426.   chtPlot.Extent.UseYMax := true;
  427.   chtPlot.Extent.UseYMin := true;
  428.  
  429.   chtPlot.ExtentSizeLimit.YMax := pMaxY;
  430.   chtPlot.ExtentSizeLimit.YMin := pMinY;
  431.   chtPlot.ExtentSizeLimit.XMax := pMaxX;
  432.   chtPlot.ExtentSizeLimit.XMin := pMinX;
  433.   chtPlot.ExtentSizeLimit.UseXMax := true;
  434.   chtPlot.ExtentSizeLimit.UseXMin := true;
  435.   chtPlot.ExtentSizeLimit.UseYMax := true;
  436.   chtPlot.ExtentSizeLimit.UseYMin := true;
  437.  
  438. end;
  439.  
  440. function TForm1.RGB(r, g, b: Byte): TColor;
  441. begin
  442.   Result := (Integer(r) or (Integer(g) shl 8) or (Integer(b) shl 16));
  443. end;
  444.  
  445. function TForm1.ColorStrToColor(WebColor: string): TColor;
  446. begin
  447.   Result :=
  448.     RGB(
  449.       StrToInt('$' + Copy(WebColor, 2, 2)),
  450.       StrToInt('$' + Copy(WebColor, 4, 2)),
  451.       StrToInt('$' + Copy(WebColor, 6, 2)));
  452. end;
  453.  
  454. end.
  455.  

wp

  • Hero Member
  • *****
  • Posts: 12070
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #2 on: May 27, 2024, 11:28:35 am »
The space in the chart above y=100 is caused by the fact that you want the series to display data point labels. The chart here is rather dumb, and just makes sure that there is space available for the label of the data point with largest y value - it does not "know" that there is plenty of space left because the axis limits are fixed by means of the Range property. The only thing you could do is to switch the series.MarkPositions to lmpInside - this means that the labels are drawn "inside" the area convered by the data values. Since now there definitely is space for all labels, there is no need to expand the axis limits. But you have no control of whether the label is drawn above or below the data point - I forgot the algorithm how this is decided, and the situation here is even more complicated because the axis limit are frozen by means of the chart's Extent property.

sdawilson

  • New Member
  • *
  • Posts: 14
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #3 on: May 27, 2024, 11:48:42 am »
Bingo! Excellent, thanks wp!!! That fixes the problem with the chart scales.

Did you have any further thoughts / guidance regarding the colour of the legend items?

wp

  • Hero Member
  • *****
  • Posts: 12070
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #4 on: May 27, 2024, 12:05:43 pm »
Sorry, I forgot...

Add the following code to where pSeriesXY is created, and add unit TALegend to "uses":
Code: Pascal  [Select][+][-]
  1.         pSeriesXY.Legend.Multiplicity := lmPoint;

BTW, this kind of makes the series labels obsolete, which also fixes the "space above y=100" issue.

sdawilson

  • New Member
  • *
  • Posts: 14
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #5 on: May 27, 2024, 12:25:32 pm »
wp, you truly are a hero! It worked first time!

You're right about the label negating the need for the legend, but I wanted to give the option of one or the other, or both. Academics are fanatical about things like that.

I really love Lazarus and the charting is amazing... although a little complicated for us newbies. This little bit of code fixes main charting shortfall in MS Access. I'll move on to adding labelled pie charts later (another irritating shortfall)... but for now, thank you very much!

wp

  • Hero Member
  • *****
  • Posts: 12070
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #6 on: May 27, 2024, 12:29:26 pm »
I'll move on to adding labelled pie charts later (another irritating shortfall)... but for now, thank you very much!
There are some demos on pie series in the (lazarus)/components/tachart/demo folders.

sdawilson

  • New Member
  • *
  • Posts: 14
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #7 on: May 27, 2024, 02:52:58 pm »
It was all going so well and then I got an "Access Violation". I have no idea how this happened... or what it is.

wp, if you're still around, can you help? I've attached the reported error.

wp

  • Hero Member
  • *****
  • Posts: 12070
Re: Plotting Scatter & Bubble Charts From Text File
« Reply #8 on: May 27, 2024, 03:00:16 pm »
"Access violation" usually means: "you're accessing an object which does not exist". So, did you create the FBackImage, and has some image file been loaded into it so that FBackImage.Graphic exists, too? (Not sure about the second condition, though). Put a breakpoint at the offending line ("ACanvas.Stretchdraw(...)"), run the application; when it stops at the breakpoint, move the mouse over the word "FBackImage" --> the popup hint tells you whether the variable is nil. If it is, it may help to check for nil before accessing it:
Code: Pascal  [Select][+][-]
  1. if (FBackImage <> nil) and (FBackImage.Graphic <> nil) then
  2.   ACanvas.StretchDraw(ARect, FBackImage.Graphic);

 

TinyPortal © 2005-2018