fpspreadsheet: xlsx chart reader supports error bars.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9136 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-01-10 23:27:55 +00:00
parent 988772c25a
commit 168a151e65
3 changed files with 124 additions and 28 deletions

View File

@ -404,6 +404,7 @@ type
FRange: Array[0..1] of TsChartRange;
FValue: Array[0..1] of Double; // 0 = positive, 1 = negative error bar value
FShow: Array[0..1] of Boolean;
FShowEndCap: Boolean;
function GetRange(AIndex: Integer): TsChartRange;
function GetShow(AIndex: Integer): Boolean;
function GetValue(AIndex: Integer): Double;
@ -430,6 +431,7 @@ type
property RangePos: TsChartRange index 0 read GetRange write SetRange;
property RangeNeg: TsChartRange index 1 read GetRange write SetRange;
property Series: TsChartSeries read FSeries;
property ShowEndCap: Boolean read FShowEndCap write FShowEndCap;
property ShowPos: Boolean index 0 read GetShow write SetShow;
property ShowNeg: Boolean index 1 read GetShow write SetShow;
property ValuePos: Double index 0 read GetValue write SetValue;
@ -1602,6 +1604,7 @@ begin
FRange[1] := TsChartRange.Create(ASeries.Chart);
FShow[0] := false;
FShow[1] := false;
FShowEndCap := true;
end;
destructor TsChartErrorBars.Destroy;
@ -1622,6 +1625,7 @@ begin
FRange[1].CopyFrom(TsChartErrorBars(ASource).RangeNeg);
FShow[0] := TsChartErrorBars(ASource).ShowPos;
FShow[1] := TsChartErrorBars(ASource).ShowNeg;
FShowEndCap := TsChartErrorBars(ASource).ShowEndCap;
FValue[0] := TsChartErrorBars(ASource).ValuePos;
FValue[1] := TsChartErrorBars(ASource).ValueNeg;
FLine.CopyFrom(TsChartErrorBars(ASource).Line);

View File

@ -39,6 +39,7 @@ type
procedure ReadChartLineSeries(ANode: TDOMNode; AChart: TsChart);
procedure ReadChartPlotArea(ANode: TDOMNode; AChart: TsChart);
procedure ReadChartScatterSeries(ANode: TDOMNode; AChart: TsChart);
procedure ReadChartSeriesErrorBars(ANode: TDOMNode; ASeries: TsChartSeries);
procedure ReadChartSeriesLabels(ANode: TDOMNode; ASeries: TsChartSeries);
procedure ReadChartSeriesMarker(ANode: TDOMNode; ASeries: TsCustomLineSeries);
procedure ReadChartSeriesProps(ANode: TDOMNode; ASeries: TsChartSeries);
@ -725,10 +726,10 @@ begin
end;
{@@ ----------------------------------------------------------------------------
Reads the properties of a line series
Reads the properties of the series marker
@@param ANode Points to the <c:marker> subnode of <c:ser> node
@@param ASeries Instance of the TsLineSeries created by ReadChartLineSeries
@@param ASeries Instance of the TsCustomLineSeries created by ReadChartLineSeries
-------------------------------------------------------------------------------}
procedure TsSpreadOOXMLChartReader.ReadChartSeriesMarker(ANode: TDOMNode; ASeries: TsCustomLineSeries);
var
@ -854,6 +855,96 @@ begin
end;
end;
{@@ ----------------------------------------------------------------------------
Reads the error bar parameters of a series.
@@param ANode Is the first child of the <c:errBars> subnode of <c:ser>.
@@param ASeries Series to which the error bars belong.
-------------------------------------------------------------------------------}
procedure TsSpreadOOXMLChartReader.ReadChartSeriesErrorBars(ANode: TDOMNode;
ASeries: TsChartSeries);
var
nodeName, s: String;
node: TDOMNode;
val: Double;
errorBars: TsChartErrorBars = nil;
part: String = '';
begin
if ANode = nil then
exit;
// We must first find out whether the node is for x or y error bars and
// whether it is for positive, negative or both error parts.
node := ANode;
while Assigned(node) do
begin
nodeName := node.NodeName;
s := GetAttrValue(node, 'val');
case nodeName of
'c:errDir':
begin
case s of
'x': errorBars := ASeries.XErrorBars;
'y': errorBars := ASeries.YErrorBars;
end;
end;
'c:errBarType':
part := s;
end;
if (errorBars <> nil) and (part <> '') then
break;
node := node.NextSibling;
end;
errorBars.ShowPos := (part = 'both') or (part = 'plus');
errorBars.ShowNeg := (part = 'both') or (part = 'minus');
node := ANode;
while Assigned(node) do
begin
nodeName := node.NodeName;
s := GetAttrValue(node, 'val');
case nodeName of
'c:errValType':
case s of
'fixedVal': errorBars.Kind := cebkConstant;
'percentage': errorBars.Kind := cebkPercentage;
'cust': errorBars.Kind := cebkCellRange;
'stdDev': errorBars.Visible := false; // not supported
'stdErr': errorBars.Visible := false; // not supported
end;
'c:val':
if (s <> '') and TryStrToFloat(s, val, FPointSeparatorSettings) then
case part of
'both':
begin
errorBars.ValuePos := val;
errorBars.ValueNeg := val;
end;
'plus':
errorBars.ValuePos := val;
'minus':
errorBars.ValueNeg := val;
end;
'c:plus':
ReadChartSeriesRange(node.FirstChild, errorBars.RangePos);
'c:minus':
ReadChartSeriesRange(node.FirstChild, errorBars.RangeNeg);
'c:spPr':
ReadChartLineProps(node.FirstChild, ASeries.Chart, errorBars.Line);
'c:noEndCap':
errorBars.ShowEndCap := (s <> '1');
end;
node := node.NextSibling;
end;
end;
{@@ ----------------------------------------------------------------------------
Reads the labels assigned to the series data points.
@@param ANode Is the first child of the <c:dLbls> subnode of <c:ser>.
@@param ASeries Series to which the labels belong.
-------------------------------------------------------------------------------}
procedure TsSpreadOOXMLChartReader.ReadChartSeriesLabels(ANode: TDOMNode;
ASeries: TsChartSeries);
var
@ -865,6 +956,7 @@ begin
while Assigned(ANode) do
begin
nodeName := ANode.NodeName;
s := GetAttrValue(ANode, 'val');
case nodeName of
'c:spPr':
ReadChartFillAndLineProps(ANode.FirstChild, ASeries.Chart, ASeries.LabelBackground, ASeries.LabelBorder);
@ -888,35 +980,20 @@ begin
end;
end;
'c:showLegendKey':
begin
s := GetAttrValue(ANode, 'val');
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlSymbol];
end;
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlSymbol];
'c:showVal':
begin
s := GetAttrValue(ANode, 'val');
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlValue];
end;
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlValue];
'c:showCatName':
begin
s := GetAttrValue(ANode, 'val');
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlCategory];
end;
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlCategory];
'c:showSerName':
begin
s := GetAttrValue(ANode, 'val');
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlSeriesName];
end;
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlSeriesName];
'c:showPercent':
begin
s := GetAttrValue(ANode, 'val');
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlPercentage];
end;
if (s <> '') and (s <> '0') then
ASeries.DataLabels := ASeries.DataLabels + [cdlPercentage];
'c:showBubbleSize':
;
'c:showLeaderLines':
@ -954,6 +1031,8 @@ begin
ReadChartSeriesLabels(ANode.Firstchild, ASeries);
'c:trendline':
ReadChartSeriesTrendLine(ANode.FirstChild, ASeries);
'c:errBars':
ReadChartSeriesErrorBars(ANode.FirstChild, ASeries);
'c:invertIfNegative':
;
'c:extLst':

View File

@ -2112,6 +2112,7 @@ end;
procedure TsWorkbookChartLink.UpdateChartErrorBars(AWorkbookSeries: TsChartSeries;
ASeries: TBasicPointSeries);
const
EPS = 1E-16;
ERRORBAR_KINDS: array[TsChartErrorBarKind] of TChartErrorBarKind = (
ebkConst, ebkPercent, ebkChartSource);
@ -2137,13 +2138,19 @@ begin
// TAChart supports error bars only for single-values sources!
if source.XCount = 1 then
begin
series.XErrorBars.Visible := AWorkbookSeries.XErrorBars.ShowPos or AWorkbookSeries.XErrorBars.ShowNeg;;
series.XErrorBars.Visible := AWorkbookSeries.XErrorBars.ShowPos or AWorkbookSeries.XErrorBars.ShowNeg;
UpdateChartPen(AWorkbookSeries.Chart, AWorkbookSeries.XErrorBars.Line, series.XErrorBars.Pen);
source.XErrorBarData.Kind := ERRORBAR_KINDS[AWorkbookSeries.XErrorBars.Kind];
source.XErrorBarData.ValuePlus := AWorkbookSeries.XErrorBars.ValuePos;
source.XErrorBarData.ValueMinus := AWorkbookSeries.XErrorBars.ValueNeg;
if not AWorkbookSeries.XErrorBars.ShowPos then
source.XErrorBarData.ValuePlus := EPS; // Note: 0 would mean "no error bar at all" for TAChart!
if not AWorkbookSeries.XErrorBars.ShowNeg then
source.XErrorBarData.ValueMinus := EPS;
if (AWorkbookSeries.XErrorBars.Kind = cebkCellRange) then
source.SetXErrorBarRange(AWorkbookSeries.XErrorBars.RangePos, AWorkbookSeries.XErrorBars.RangeNeg);
if not AWorkbookSeries.XErrorBars.ShowEndCap then
series.XErrorBars.Width := 0;
end;
if source.YCount = 1 then
@ -2153,8 +2160,14 @@ begin
source.YErrorBarData.Kind := ERRORBAR_KINDS[AWorkbookSeries.YErrorBars.Kind];
source.YErrorBarData.ValuePlus := AWorkbookSeries.YErrorBars.ValuePos;
source.YErrorBarData.ValueMinus := AWorkbookSeries.YErrorBars.ValueNeg;
if not AWorkbookSeries.YErrorBars.ShowPos then
source.YErrorBarData.ValuePlus := EPS;
if not AWorkbookSeries.YErrorBars.ShowNeg then
source.YErrorBarData.ValueMinus := EPS;
if (AWorkbookSeries.YErrorBars.Kind = cebkCellRange) then
source.SetYErrorBarRange(AWorkbookSeries.YErrorBars.RangePos, AWorkbookSeries.YErrorBars.RangeNeg);
if not AWorkbookSeries.YErrorBars.ShowEndCap then
series.YErrorBars.Width := 0;
end;
end;