YOUSEFUL / DELPHI

Atlas Plot Pro: FAQ, examples and tutorial

Learning center · Component reference

Atlas Plot Pro adds statistics and three specialised chart controls to Atlas Plot: TAtlasBoxPlot (box-and-whisker), TAtlasHeatMap (a value matrix as coloured cells) and TAtlasPolarPlot (a radar chart), plus TAtlasPlotAnalysis, which adds a histogram, a polynomial fit or a smoothed copy as a new series of an Atlas Plot TAtlasPlot. Add Atlas.PlotStats for TAtlasStats, Atlas.BoxPlot, Atlas.HeatMap or Atlas.PolarPlot for a control, Atlas.PlotAnalysis for analysis series, and Atlas.PlotAxis when you catch EAtlasPlotError, which every unit raises on invalid input. The product requires Atlas Plot, which supplies the axis, the drawing surface and TAtlasPlot; the three controls are on the Atlas Plot palette page. It is VCL only and has been built for Win32 and Win64.

FAQ

What does Atlas Plot Pro add to Atlas Plot? Chart forms that TAtlasPlot cannot draw (box plots, heat maps, radar charts), statistics as plain functions that need no chart, and helpers that add computed series to a TAtlasPlot. It is a separate package on top of Atlas Plot and does not change TAtlasPlot itself. Line, bar, area, point and pie series, axes, zoom and export of general charts remain Atlas Plot's job; see its component reference.

Which packages do I need at run time? Either link the units into your program (the samples do this, and then no packages are needed), or build with runtime packages: AtlasPlotProRuntime370.bpl needs AtlasPlotRuntime370.bpl beside it or on PATH. The design package AtlasPlotProDesign registers TAtlasBoxPlot, TAtlasHeatMap and TAtlasPolarPlot on the Atlas Plot page. It compiles, but installing it in the IDE and using the controls in the form designer have not been tested; creating the controls in code is the verified path. See Getting started for building the packages.

How are quartiles, whiskers and outliers defined? Quantiles interpolate linearly between closest ranks: position (n - 1) * Fraction in the sorted values, the common "type 7" (spreadsheet QUARTILE.INC). Whiskers end at the most extreme values within 1.5 interquartile ranges of the box, and anything beyond is an outlier. TAtlasStats.Box computes all of it and TAtlasBoxPlot draws exactly those numbers. The standard deviation is the sample one (divides by n - 1).

How does the histogram choose its bins? Pass a bin count from 1 to 10000, or 0 for Sturges' rule, ceil(log2 n) + 1. Bins have equal width from the minimum to the maximum and are half open, [edge, next edge), except the last, which includes the maximum. When all values are equal there is one bin of width one centred on them. BinOf finds a value's bin by binary search, so values exactly on an edge land in the right bin.

Why does PolynomialFit raise, and can I fit dates? Degrees 1 to 6 are supported, and a fit needs at least Degree + 1 points with more distinct X values than the degree; otherwise it raises EAtlasPlotError with "A fit needs more distinct arguments than its degree". Arguments are scaled to -1..1 before fitting, so TDateTime values (around 46,000) and other large arguments give accurate fits. The coefficients are not exposed; use Evaluate, which also extrapolates.

How do I show missing values? In a heat map and a radar chart, use NaN: it is an empty cell (drawn in EmptyColor) or a gap in the radar outline. The statistics functions and TAtlasBoxPlot.AddCategory reject NaN, infinite values and magnitudes above 1E100 with EAtlasPlotError, so filter missing values out of a sample first. Nothing is ever changed by a rejected call.

Can I render charts without showing a window? Yes. Create a control with no owner and no parent and call SaveToFile (.png, .bmp, .emf or .svg), CreateBitmap, ToSVG, RenderTo or Render. This works in console programs and services; the Statistics console sample does it. A file is written beside the target and renamed, so a failed export keeps the old file.

Do hit tests still match the screen after an export? Yes. HitTest, ValueToPixel, CellRect, PlotRect, Centre and Radius describe the most recent render. After SaveToFile, CreateBitmap, ToSVG or RenderTo, a control with a window handle re-runs its on-screen layout, so an 800 x 500 export does not break mouse handling on a smaller control. Only Render into your own surface leaves the queries describing that render.

Can I write my own chart type? Yes: derive from TAtlasCustomChart and override DrawChart and ResetLayout. You get the title, footnote, legend helper, batching and all export formats. The custom chart type sample writes one with hit testing.

Is any of it thread-safe? The TAtlasStats functions keep no state and never modify their input, so they can be called from any thread. The controls are VCL controls with no locking; create, change and render them from the main thread.

What is not implemented? No FMX version and no Win64 ARM (ARM64EC) build: only Win32 and Win64 were compiled. Also missing: secondary or multiple value axes (they need changes inside TAtlasPlot); bubble, error bar, range/Gantt bar, waterfall, funnel, gauge, contour, surface and other 3D series; true angle/radius polar series and wind roses (the radar chart is category based); annotations, cursors, draggable points, animations, gradients and transparency (the drawing surface has no alpha, so radar fills are opaque); data-aware series; a chart editor dialog; print preview. High-DPI switching, VCL styles, printing and accessibility have not been tested.

Examples

Summarise a sample with TAtlasStats: quartiles, whiskers at 1.5 IQR, outliers and the sample standard deviation.

pascal
uses
  System.SysUtils, Atlas.PlotStats;

function Summary(const Values: TArray<Double>): string;
var Box: TAtlasBoxStats;
begin
  Box := TAtlasStats.Box(Values);
  Result := Format('Q1 %.2f, median %.2f, Q3 %.2f, whiskers %.0f..%.0f, %d outlier(s), sd %.2f',
    [Box.Q1, Box.Median, Box.Q3, Box.LowWhisker, Box.HighWhisker, Length(Box.Outliers),
     TAtlasStats.StdDev(Values)]);
end;

For Summary([1, 2, 3, 4, 5, 6, 7, 8, 9, 40]) the result is Q1 3.25, median 5.50, Q3 7.75, whiskers 1..9, 1 outlier(s), sd 11.37; 40 lies beyond Q3 + 1.5 * IQR = 14.5.

Count values in histogram bins and find the bin of a value. The last bin includes the maximum.

pascal
uses
  System.SysUtils, System.Classes, Atlas.PlotStats;

procedure ListBins(Target: TStrings);
var Bars: TAtlasHistogram; I: Integer;
begin
  Bars := TAtlasStats.Histogram([1, 2, 2, 3, 3, 3, 4, 4, 5], 4);
  for I := 0 to High(Bars.Counts) do
    Target.Add(Format('%g..%g: %d', [Bars.Edges[I], Bars.Edges[I + 1], Bars.Counts[I]]));
  Target.Add(Format('4.5 is in bin %d', [Bars.BinOf(4.5)]));
end;

Adds 1..2: 1, 2..3: 2, 3..4: 3, 4..5: 3 and 4.5 is in bin 3. With Bins 0 the same data gets five bins (Sturges' rule, ceil(log2 9) + 1).

Fit a quadratic and extrapolate. The fit is computed on arguments scaled to -1..1, so it also works for date values.

pascal
uses
  System.SysUtils, Atlas.PlotStats;

function Extrapolate: string;
var Fit: TAtlasPolynomial;
begin
  Fit := TAtlasStats.PolynomialFit([-2, -1, 0, 1, 2], [9, 3, 1, 3, 9], 2);
  Result := Format('degree %d, R squared %.3f, f(3) = %.3f', [Fit.Degree, Fit.RSquared, Fit.Evaluate(3)]);
end;

Returns degree 2, R squared 1.000, f(3) = 19.000: the points lie exactly on y = 2x² + 1.

Put a box plot on a form. Each call to AddCategory summarises its sample at once.

pascal
uses
  Vcl.Controls, Vcl.Forms, Atlas.BoxPlot;

function AddBoxPlot(Form: TForm): TAtlasBoxPlot;
begin
  Result := TAtlasBoxPlot.Create(Form);
  Result.Parent := Form;
  Result.Align := alClient;
  Result.Title := 'Response time by service';
  Result.ValueAxis.Title := 'ms';
  Result.AddCategory('Gateway', [12, 15, 15, 17, 18, 18, 18, 20, 21, 60]);
  Result.AddCategory('Search', [30, 31, 35, 36, 38, 40, 41, 45]);
end;

Two boxes on an automatic value axis from 10 to 60; the Gateway box spans 15.5 to 19.5 with its median at 18, and 60 is drawn as a hollow outlier circle.

Show a matrix as a heat map with a three-stop gradient, a fixed scale and an empty cell.

pascal
uses
  System.Math, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Atlas.HeatMap;

function AddHeatMap(Form: TForm): TAtlasHeatMap;
begin
  Result := TAtlasHeatMap.Create(Form);
  Result.Parent := Form;
  Result.Align := alClient;
  Result.RowLabels.CommaText := 'Mon,Tue,Wed';
  Result.ColumnLabels.CommaText := '00,06,12,18';
  Result.LoadMatrix([[12, 35, 80, 44], [10, NaN, 85, 40], [15, 38, 91, 52]]);
  Result.LowColor := clWhite;
  Result.MidColor := clYellow;
  Result.HighColor := clRed;
  Result.SetScale(0, 100);
  Result.ShowValues := True;
end;

A 3 x 4 grid from white through yellow (50) to red (100), values printed in black or white where they fit, Tuesday 06 in light grey, and a 0..100 colour bar on the right.

Compare two series on a radar chart. Set the categories first; NaN leaves a gap.

pascal
uses
  System.Math, Vcl.Controls, Vcl.Forms, Atlas.PolarPlot;

function AddRadar(Form: TForm): TAtlasPolarPlot;
begin
  Result := TAtlasPolarPlot.Create(Form);
  Result.Parent := Form;
  Result.Align := alClient;
  Result.RadialAxis.SetRange(0, 10);
  Result.Categories.CommaText := 'Speed,Reliability,Price,Support,"Ease of use"';
  Result.AddSeries('Release 4', [7, 8.5, 6, 5, 7.5]);
  Result.AddSeries('Release 5', [8.5, 9, 6.5, NaN, 8]);
end;

A pentagon grid with Speed at twelve o'clock, a filled blue shape for Release 4 and an open orange outline for Release 5 with a gap at Support (a series with a gap is not filled), and a legend on the right.

Add a polynomial fit and a smoothed line to measured points on a TAtlasPlot.

pascal
uses
  System.SysUtils, Vcl.Controls, Vcl.Forms, Atlas.PlotSeries, Atlas.Plot, Atlas.PlotAnalysis;

function AddFittedPlot(Form: TForm): TAtlasPlot;
var Measured: TAtlasPlotSeries; I: Integer; RSquared: Double;
begin
  Result := TAtlasPlot.Create(Form);
  Result.Parent := Form;
  Result.Align := alClient;
  Measured := Result.AddSeries('Measured', pkPoint);
  for I := 0 to 40 do
    Measured.Add(I / 4, 3 + 0.9 * (I / 4) - 0.35 * Sqr(I / 4 - 5) + 1.5 * Sin(I * 2.3));
  TAtlasPlotAnalysis.AddPolynomialFit(Result, Measured, 2, RSquared).LineWidth := 3;
  TAtlasPlotAnalysis.AddExponentialSmoothing(Result, Measured, 0.3);
  Result.Title := Format('Quadratic fit, R squared %.3f', [RSquared]);
end;

Three series: the points, a 64-point curve titled Measured fit(2) and Measured smooth(0.3); the title reads Quadratic fit, R squared 0.928.

Export a chart from a console program or service; no window is needed.

pascal
uses
  Atlas.BoxPlot;

procedure ExportBoxPlot;
var Box: TAtlasBoxPlot;
begin
  Box := TAtlasBoxPlot.Create(nil);
  try
    Box.AddCategory('Gateway', [12, 15, 15, 17, 18, 18, 18, 20, 21, 60]);
    Box.SaveToFile('box.png', 800, 500);
    Box.SaveToFile('box.svg', 800, 500);
    Box.SaveToFile('box.emf', 800, 500);
  finally
    Box.Free;
  end;
end;

Writes box.png, box.svg and box.emf. Any other extension raises EAtlasPlotError before a file is touched.

Tutorial

Sample project: Statistics console.

Goal: compute quantiles, box statistics, a histogram, a correlation, polynomial fits and a smoothed series on data whose answers you know, check every result, and save a heat map and a box plot to files without opening a window.

  1. Create a console application in Delphi 13 and save it as StatisticsConsole.dpr. Add the Atlas Plot Pro src folder and the Atlas Plot src folder to the project's search path. Replace the generated code with the program header, the uses clause and two helpers: Expect raises when a result is wrong and prints ok: when it is right, Near compares doubles.
pascal
program StatisticsConsole;

// Atlas Plot Pro quick start: statistics on known data, then a heat map
// and a box plot written to image files without a window. Every result is
// checked against a value worked out by hand; a mismatch raises and the
// program exits with code 1.

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.StrUtils, System.Math, System.IOUtils, Winapi.Windows, Vcl.Graphics,
  Atlas.PlotAxis, Atlas.PlotStats, Atlas.BoxPlot, Atlas.HeatMap;

procedure Expect(Condition: Boolean; const What: string);
begin
  if not Condition then raise Exception.Create('Unexpected result: ' + What);
  Writeln('  ok: ', What);
end;

function Near(const A, B: Double): Boolean;
begin
  Result := Abs(A - B) < 1E-9;
end;
  1. Add the quantiles. Quantile sorts a copy of the values and interpolates between closest ranks, so 1..10 in any order has its first quartile at 3.25.
pascal
procedure Quantiles;
var Values: TArray<Double>;
begin
  Writeln('Quantiles of 1..10 in shuffled order');
  Values := [7, 2, 9, 4, 5, 3, 8, 6, 1, 10];
  Writeln(Format('  Q1 %.2f  median %.2f  Q3 %.2f  mean %.2f  sd %.4f',
    [TAtlasStats.Quantile(Values, 0.25), TAtlasStats.Quantile(Values, 0.5),
     TAtlasStats.Quantile(Values, 0.75), TAtlasStats.Mean(Values), TAtlasStats.StdDev(Values)]));
  Expect(Near(TAtlasStats.Quantile(Values, 0.25), 3.25), 'first quartile interpolates between ranks 3 and 4');
  Expect(Near(TAtlasStats.Quantile(Values, 0.5), 5.5), 'median of an even count is the middle pair average');
  Expect(Near(TAtlasStats.Mean(Values), 5.5) and Near(TAtlasStats.StdDev(Values), Sqrt(82.5 / 9)), 'mean and sample standard deviation');
end;
  1. Add the box statistics. TAtlasStats.Box returns quartiles, whiskers and outliers in one record; 40 lies beyond Q3 + 1.5 * IQR = 14.5, so the high whisker stops at 9.
pascal
procedure BoxStatistics;
var Box: TAtlasBoxStats;
begin
  Writeln('Box statistics of 1..9 and one slow value, 40');
  Box := TAtlasStats.Box([1, 2, 3, 4, 5, 6, 7, 8, 9, 40]);
  Writeln(Format('  box %.2f..%.2f  whiskers %.0f..%.0f  %d outlier(s)',
    [Box.Q1, Box.Q3, Box.LowWhisker, Box.HighWhisker, Length(Box.Outliers)]));
  Expect(Near(Box.Q1, 3.25) and Near(Box.Median, 5.5) and Near(Box.Q3, 7.75), 'quartiles');
  Expect(Near(Box.LowWhisker, 1) and Near(Box.HighWhisker, 9), 'whiskers stop at the last value within 1.5 IQR');
  Expect((Length(Box.Outliers) = 1) and Near(Box.Outliers[0], 40), '40 is the only outlier');
end;
  1. Add the histogram. Four bins over 1..5 are [1, 2), [2, 3), [3, 4) and [4, 5]; BinOf returns -1 outside them, and Bins 0 applies Sturges' rule.
pascal
procedure Histogram;
var Bars: TAtlasHistogram; I: Integer;
begin
  Writeln('Histogram of 1 2 2 3 3 3 4 4 5 in four bins');
  Bars := TAtlasStats.Histogram([1, 2, 2, 3, 3, 3, 4, 4, 5], 4);
  for I := 0 to High(Bars.Counts) do
    Writeln(Format('  [%.0f, %.0f%s: %d', [Bars.Edges[I], Bars.Edges[I + 1], IfThen(I = High(Bars.Counts), ']', ')'), Bars.Counts[I]]));
  Expect((Bars.Counts[0] = 1) and (Bars.Counts[1] = 2) and (Bars.Counts[2] = 3) and (Bars.Counts[3] = 3), 'bins are half open, the last one closed');
  Expect((Bars.BinOf(2) = 1) and (Bars.BinOf(5) = 3) and (Bars.BinOf(6) = -1), 'BinOf finds the bin; outside the range is -1');
  Expect(Length(TAtlasStats.Histogram([1, 2, 2, 3, 3, 3, 4, 4, 5]).Counts) = 5, 'Bins = 0 applies Sturges'' rule: ceil(log2 9) + 1 = 5');
end;
  1. Add the correlation and the two polynomial fits. The quadratic goes exactly through a parabola, so it also predicts 171 at x = 10, well outside the data; a straight line through the same points has an R squared of about 0.30.
pascal
procedure CorrelationAndFit;
var X, Y: TArray<Double>; I: Integer; Fit: TAtlasPolynomial;
begin
  Writeln('Correlation and least-squares fits');
  Writeln(Format('  r = %.2f', [TAtlasStats.Correlation([1, 2, 3, 4, 5], [2, 1, 4, 3, 5])]));
  Expect(Near(TAtlasStats.Correlation([1, 2, 3, 4, 5], [2, 1, 4, 3, 5]), 0.8), 'Pearson r of a partly ordered pair is 0.8');
  SetLength(X, 9);
  SetLength(Y, 9);
  for I := 0 to 8 do
  begin
    X[I] := I - 4;
    Y[I] := 2 * X[I] * X[I] - 3 * X[I] + 1;   // an exact parabola
  end;
  Fit := TAtlasStats.PolynomialFit(X, Y, 2);
  Writeln(Format('  quadratic: degree %d, R squared %.4f, value at 10 = %.4f', [Fit.Degree, Fit.RSquared, Fit.Evaluate(10)]));
  Expect((Fit.Degree = 2) and Near(Fit.RSquared, 1) and (Abs(Fit.Evaluate(10) - 171) < 1E-6), 'the parabola is recovered, also outside the data');
  Fit := TAtlasStats.PolynomialFit(X, Y, 1);
  Writeln(Format('  straight line: R squared %.4f', [Fit.RSquared]));
  Expect(Abs(Fit.RSquared - 0.30474) < 1E-4, 'a line explains less than a third of a parabola');
end;
  1. Add the smoothing and a rejected call. Every function raises EAtlasPlotError instead of returning NaN; two points cannot fix a parabola.
pascal
procedure Smoothing;
var Smooth: TArray<Double>;
begin
  Writeln('Exponential smoothing of 10 20 20 0 with alpha 0.5');
  Smooth := TAtlasStats.ExponentialSmoothing([10, 20, 20, 0], 0.5);
  Writeln(Format('  %.2f %.2f %.2f %.2f', [Smooth[0], Smooth[1], Smooth[2], Smooth[3]]));
  Expect(Near(Smooth[0], 10) and Near(Smooth[1], 15) and Near(Smooth[2], 17.5) and Near(Smooth[3], 8.75), 'each value moves halfway towards the input');
end;

procedure Rejections;
var Rejected: Boolean;
begin
  Writeln('Invalid input raises EAtlasPlotError');
  Rejected := False;
  try
    TAtlasStats.PolynomialFit([1, 2], [3, 4], 2);   // two points cannot fix a parabola
  except
    on EAtlasPlotError do Rejected := True;
  end;
  Expect(Rejected, 'a fit with too few distinct arguments is rejected');
end;
  1. Draw two charts without a window. A control created with no owner and no parent renders straight into a file; ColorOf shows where a value falls on the fixed 50..100 scale.
pascal
procedure ExportCharts;
var Map: TAtlasHeatMap; Box: TAtlasBoxPlot; Svg: string;
begin
  Writeln('Charts rendered without a window');
  Map := TAtlasHeatMap.Create(nil);
  try
    Map.Title := 'Cache hit rate (%) by node and hour';
    Map.RowLabels.CommaText := 'node-a,node-b,node-c';
    Map.ColumnLabels.CommaText := '00,06,12,18';
    // Percentages, because FormatFloat prints a % sign as it is and does not multiply by 100.
    Map.LoadMatrix([[91, 88, 72, 80], [93, NaN, 75, 83], [89, 86, 70, 79]]);
    Map.LowColor := clWhite;
    Map.HighColor := clBlack;
    Map.SetScale(50, 100);
    Map.ShowValues := True;
    Map.ValueFormat := '0"%"';
    Map.SaveToFile('heatmap.png', 480, 300);
    Expect(TFile.GetSize('heatmap.png') > 1000, 'heatmap.png written');
    Expect(Map.IsEmpty(1, 1) and (Map.ColorOf(75) = TColor(RGB(128, 128, 128))), 'NaN is an empty cell; 75 is halfway on the 50..100 scale');
  finally
    Map.Free;
  end;
  Box := TAtlasBoxPlot.Create(nil);
  try
    Box.Title := 'Response time by service';
    Box.ValueAxis.Title := 'ms';
    Box.AddCategory('Gateway', [12, 15, 15, 17, 18, 18, 18, 20, 21, 60]);
    Box.AddCategory('Search', [30, 31, 35, 36, 38, 40, 41, 45]);
    Box.SaveToFile('boxplot.svg', 480, 300);
    Svg := TFile.ReadAllText('boxplot.svg', TEncoding.UTF8);
    Expect((Pos('>Gateway<', Svg) > 0) and (Pos('>Search<', Svg) > 0), 'boxplot.svg names both categories');
    Expect(Near(Box.Stats(0).Median, 18) and (Length(Box.Stats(0).Outliers) = 1), 'the chart keeps the statistics of each sample');
  finally
    Box.Free;
  end;
end;
  1. Finish with the main block, which runs every step and turns any exception into exit code 1.
pascal
begin
  try
    FormatSettings := TFormatSettings.Invariant;
    Quantiles;
    BoxStatistics;
    Histogram;
    CorrelationAndFit;
    Smoothing;
    Rejections;
    ExportCharts;
    Writeln('All results verified.');
  except
    on E: Exception do
    begin
      Writeln(E.ClassName, ': ', E.Message);
      ExitCode := 1;
    end;
  end;
end.
  1. Build and run it. The program prints every result followed by its check and exits with code 0:
Example
Quantiles of 1..10 in shuffled order
  Q1 3.25  median 5.50  Q3 7.75  mean 5.50  sd 3.0277
  ok: first quartile interpolates between ranks 3 and 4
  ok: median of an even count is the middle pair average
  ok: mean and sample standard deviation
Box statistics of 1..9 and one slow value, 40
  box 3.25..7.75  whiskers 1..9  1 outlier(s)
  ok: quartiles
  ok: whiskers stop at the last value within 1.5 IQR
  ok: 40 is the only outlier
Histogram of 1 2 2 3 3 3 4 4 5 in four bins
  [1, 2): 1
  [2, 3): 2
  [3, 4): 3
  [4, 5]: 3
  ok: bins are half open, the last one closed
  ok: BinOf finds the bin; outside the range is -1
  ok: Bins = 0 applies Sturges' rule: ceil(log2 9) + 1 = 5
Correlation and least-squares fits
  r = 0.80
  ok: Pearson r of a partly ordered pair is 0.8
  quadratic: degree 2, R squared 1.0000, value at 10 = 171.0000
  ok: the parabola is recovered, also outside the data
  straight line: R squared 0.3047
  ok: a line explains less than a third of a parabola
Exponential smoothing of 10 20 20 0 with alpha 0.5
  10.00 15.00 17.50 8.75
  ok: each value moves halfway towards the input
Invalid input raises EAtlasPlotError
  ok: a fit with too few distinct arguments is rejected
Charts rendered without a window
  ok: heatmap.png written
  ok: NaN is an empty cell; 75 is halfway on the 50..100 scale
  ok: boxplot.svg names both categories
  ok: the chart keeps the statistics of each sample
All results verified.

heatmap.png and boxplot.svg are next to the executable. To adapt it, replace the arrays with your own measurements and turn the Expect calls into Writeln; then give the box plot a parent on a form, as the Pro charts in a window sample does, and move the mouse over it to read the statistics back through HitTest.

Troubleshooting

EAtlasPlotError: At least 2 value(s) are needed; 1 given. StdDev and Correlation need two values; Mean, Quantile, Box, Histogram and ExponentialSmoothing need one; PolynomialFit needs Degree + 1. Check the length first, or catch EAtlasPlotError (unit Atlas.PlotAxis) and show "not enough data".

EAtlasPlotError: Plot values must be finite from AddCategory or a statistics function. The sample contains NaN or an infinite value. Box plots and statistics do not accept gaps; remove missing values first. Only the heat map and the radar chart treat NaN as "no value". "Plot value exceeds the supported magnitude 1E100" is the same check for huge numbers.

A fit needs more distinct arguments than its degree. Too few different X values for the degree, for example a quadratic through two dates or through repeated measurements at one X. Lower the degree or collect more distinct points.

Every heat map caption shows 1% or 0%. ValueFormat is a FormatFloat mask, and FormatFloat prints % as it is instead of multiplying by 100. Store percentages as 0..100 and use 0"%", as the quick start does.

Setting ScaleMinimum and ScaleMaximum does not change the heat map colours. They only apply while AutoScale is False, and setting them does not turn it off. Call SetScale(Low, High), which sets both and turns AutoScale off, or set AutoScale := False yourself.

Set at least three categories before adding series, or radar series vanish. Series are sized to the spokes: set Categories first, with at least three entries. Adding or removing a category afterwards discards every series; renaming one keeps them.

A filled radar series is drawn only as an outline. It contains NaN. A series with a gap has no closed shape, so it is drawn as separate line runs without a fill.

HitTest finds nothing and ValueToPixel returns -1. The chart has not been rendered yet (a control is laid out when it paints or exports), or the last target was too small to draw into. Query after the form has painted, or after CreateBitmap. Values outside a fixed ValueAxis range are clipped and cannot be hit either.

An outlier is missing from the box plot. Either ShowOutliers is False (hidden outliers are neither drawn nor hit and do not stretch the scale) or a fixed ValueAxis.SetRange excludes it. Call ValueAxis.Automatic := True to return to the automatic range.

Unsupported export format ".jpg". SaveToFile writes .png, .bmp, .emf and .svg only. Use CreateBitmap and a VCL image class for other formats.

The program does not start because AtlasPlotRuntime370.bpl is missing. Built with runtime packages, AtlasPlotProRuntime370.bpl needs the Atlas Plot runtime package beside the executable or on PATH. Ship both, or link the units statically.

Hint H2443 about TFont.GetStyle in Atlas.PlotSurface.pas. It appears when the Atlas Plot sources are compiled into your program, because that unit does not list System.UITypes. The call is just not inlined; it is harmless and does not appear when you link against the runtime packages.