fpspreadsheet: Add support for bubble series.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8991 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-10-28 12:07:10 +00:00
parent 43aa702f8e
commit 0599bd5dcd
3 changed files with 94 additions and 33 deletions

View File

@ -5,7 +5,7 @@ program write_chart_demo;
uses uses
SysUtils, fpspreadsheet, fpstypes, fpschart, xlsxooxml, fpsopendocument; SysUtils, fpspreadsheet, fpstypes, fpschart, xlsxooxml, fpsopendocument;
const const
SERIES_CLASS: TsChartSeriesClass = TsScatterSeries; SERIES_CLASS: TsChartSeriesClass = TsBubbleSeries; //TsScatterSeries;
r1 = 1; r1 = 1;
r2 = 8; r2 = 8;
var var
@ -21,18 +21,21 @@ begin
sheet1 := b.AddWorksheet('test1'); sheet1 := b.AddWorksheet('test1');
sheet1.WriteText(0, 1, '1+sin(x)'); sheet1.WriteText(0, 1, '1+sin(x)');
sheet1.WriteText(0, 2, '1+sin(x/2)'); sheet1.WriteText(0, 2, '1+sin(x/2)');
sheet1.WriteText(0, 3, 'Bubble');
for i := r1 to r2-1 do for i := r1 to r2-1 do
begin begin
sheet1.WriteNumber(i, 0, i-1); sheet1.WriteNumber(i, 0, i-1);
sheet1.WriteNumber(i, 1, 1+sin(i-1)); sheet1.WriteNumber(i, 1, 1+sin(i-1));
sheet1.WriteNumber(i, 2, 1+sin((i-1)/2)); sheet1.WriteNumber(i, 2, 1+sin((i-1)/2));
sheet1.WriteNumber(i, 3, i*i); // Bubble series radii
end; end;
sheet1.WriteNumber(r2, 0, 9); sheet1.WriteNumber(r2, 0, 9);
sheet1.WriteNumber(r2, 1, 2); sheet1.WriteNumber(r2, 1, 2);
sheet1.WriteNumber(r2, 2, 2.5); sheet1.WriteNumber(r2, 2, 2.5);
sheet1.WriteNumber(r2, 3, r2*r2);
// Create chart // Create chart
ch := b.AddChart(sheet1, 4, 4, 160, 100); ch := b.AddChart(sheet1, 4, 6, 160, 100);
// Add first series (type depending on SERIES_CLASS) // Add first series (type depending on SERIES_CLASS)
ser := SERIES_CLASS.Create(ch); ser := SERIES_CLASS.Create(ch);
@ -47,16 +50,25 @@ begin
TsLineSeries(ser).ShowSymbols := true; TsLineSeries(ser).ShowSymbols := true;
TsLineSeries(ser).Symbol := cssCircle; TsLineSeries(ser).Symbol := cssCircle;
end; end;
if (ser is TsBubbleSeries) then
begin
TsBubbleSeries(ser).SetXRange(r1, 0, r2, 0);
TsBubbleSeries(ser).SetYRange(r1, 2, r2, 2);
TsBubbleSeries(ser).SetBubbleRange(r1, 3, r2, 3);
end;
// Add second series if SERIES_CLASS <> TsBubbleSeries then
ser := SERIES_CLASS.Create(ch); begin
// ser := TsBarSeries.Create(ch); // Add second series
ser.SetTitleAddr(0, 2); ser := SERIES_CLASS.Create(ch);
ser.SetLabelRange(r1, 0, r2, 0); // ser := TsBarSeries.Create(ch);
ser.SetXRange(r1, 0, r2, 0); ser.SetTitleAddr(0, 2);
ser.SetYRange(r1, 2, r2, 2); ser.SetLabelRange(r1, 0, r2, 0);
ser.Line.Color := scRed; ser.SetXRange(r1, 0, r2, 0);
ser.Fill.FgColor := scRed; ser.SetYRange(r1, 2, r2, 2);
ser.Line.Color := scRed;
ser.Fill.FgColor := scRed;
end;
{$IFDEF DARK_MODE} {$IFDEF DARK_MODE}
ch.Background.FgColor := scBlack; ch.Background.FgColor := scBlack;
@ -71,7 +83,7 @@ begin
ch.Background.Style := fsSolidFill; ch.Background.Style := fsSolidFill;
ch.Border.Style := clsSolid; ch.Border.Style := clsSolid;
ch.PlotArea.Background.Style := fsSolidFill; ch.PlotArea.Background.Style := fsSolidFill;
//ch.RotatedAxes := true; ch.RotatedAxes := true;
ch.StackMode := csmStackedPercentage; ch.StackMode := csmStackedPercentage;
//ch.Interpolation := ciCubicSpline; //ch.Interpolation := ciCubicSpline;

View File

@ -111,7 +111,7 @@ type
end; end;
TsChartAxisPosition = (capStart, capEnd, capValue); TsChartAxisPosition = (capStart, capEnd, capValue);
TsChartType = (ctEmpty, ctBar, ctLine, ctArea, ctBarLine, ctScatter); TsChartType = (ctEmpty, ctBar, ctLine, ctArea, ctBarLine, ctScatter, ctBubble);
TsChartAxis = class(TsChartFillElement) TsChartAxis = class(TsChartFillElement)
private private
@ -239,6 +239,15 @@ type
constructor Create(AChart: TsChart); override; constructor Create(AChart: TsChart); override;
end; end;
TsBubbleSeries = class(TsChartSeries)
private
FBubbleRange: TsCellRange;
public
constructor Create(AChart: TsChart); override;
procedure SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
property BubbleRange: TsCellRange read FBubbleRange;
end;
TsChartSeriesSymbol = ( TsChartSeriesSymbol = (
cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssTriangleLeft, cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssTriangleLeft,
cssTriangleRight, cssCircle, cssStar, cssX, cssPlus, cssAsterisk cssTriangleRight, cssCircle, cssStar, cssX, cssPlus, cssAsterisk
@ -368,7 +377,7 @@ type
{ Connecting line between data points (for line and scatter series) } { Connecting line between data points (for line and scatter series) }
property Interpolation: TsChartInterpolation read FInterpolation write FInterpolation; property Interpolation: TsChartInterpolation read FInterpolation write FInterpolation;
{ x and y axes exchanged (for bar series) } { x and y axes exchanged (mainly for bar series, but works also for scatter and bubble series) }
property RotatedAxes: Boolean read FRotatedAxes write FRotatedAxes; property RotatedAxes: Boolean read FRotatedAxes write FRotatedAxes;
{ Stacking of series (for bar and area series ) } { Stacking of series (for bar and area series ) }
property StackMode: TsChartStackMode read FStackMode write FStackMode; property StackMode: TsChartStackMode read FStackMode write FStackMode;
@ -716,6 +725,23 @@ begin
FChartType := ctBar; FChartType := ctBar;
end; end;
{ TsBubbleSeries }
constructor TsBubbleSeries.Create(AChart: TsChart);
begin
inherited;
FChartType := ctBubble;
end;
procedure TsBubbleSeries.SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if (ARow1 <> ARow2) and (ACol1 <> ACol2) then
raise Exception.Create('Series bubble values can only be located in a single column or row.');
FBubbleRange.Row1 := ARow1;
FBubbleRange.Col1 := ACol1;
FBubbleRange.Row2 := ARow2;
FBubbleRange.Col2 := ACol2;
end;
{ TsLineSeries } { TsLineSeries }

View File

@ -538,7 +538,7 @@ const
); );
CHART_TYPE_NAMES: array[TsChartType] of string = ( CHART_TYPE_NAMES: array[TsChartType] of string = (
'', 'bar', 'line', 'area', 'barLine', 'scatter' '', 'bar', 'line', 'area', 'barLine', 'scatter', 'bubble'
); );
CHART_SYMBOL_NAMES: array[TsChartSeriesSymbol] of String = ( CHART_SYMBOL_NAMES: array[TsChartSeriesSymbol] of String = (
@ -7142,7 +7142,9 @@ var
sheet: TsWorksheet; sheet: TsWorksheet;
series: TsChartSeries; series: TsChartSeries;
valuesRange: String; valuesRange: String;
domainRange: String = ''; domainRangeX: String = '';
domainRangeY: String = '';
rangeStr: String = '';
titleAddr: String; titleAddr: String;
count: Integer; count: Integer;
begin begin
@ -7151,10 +7153,10 @@ begin
series := AChart.Series[ASeriesIndex]; series := AChart.Series[ASeriesIndex];
sheet := TsWorkbook(FWorkbook).GetWorksheetByIndex(AChart.sheetIndex); sheet := TsWorkbook(FWorkbook).GetWorksheetByIndex(AChart.sheetIndex);
// These are the x values of a scatter plot. // These are the x values of a scatter or bubble plot.
if series is TsScatterSeries then if (series is TsScatterSeries) or (series is TsBubbleSeries) then
begin begin
domainRange := GetSheetCellRangeString_ODS( domainRangeX := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name, sheet.Name, sheet.Name,
series.XRange.Row1, series.XRange.Col1, series.XRange.Row1, series.XRange.Col1,
series.XRange.Row2, series.XRange.Col2, series.XRange.Row2, series.XRange.Col2,
@ -7162,20 +7164,37 @@ begin
); );
end; end;
// These are the y values if series is TsBubbleSeries then
valuesRange := GetSheetCellRangeString_ODS( begin
sheet.Name, sheet.Name, domainRangeY := GetSheetCellRangeString_ODS(
series.YRange.Row1, series.YRange.Col1, sheet.Name, sheet.Name,
series.YRange.Row2, series.YRange.Col2, series.YRange.Row1, series.YRange.Col1,
rfAllRel, false series.YRange.Row2, series.YRange.Col2,
); rfAllRel, false
);
// These are the bubble radii
valuesRange := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name,
TsBubbleSeries(series).BubbleRange.Row1, TsBubbleSeries(series).BubbleRange.Col1,
TsBubbleSeries(series).BubbleRange.Row2, TsBubbleSeries(series).BubbleRange.Col2,
rfAllRel, false
);
end else
// These are the y values of the non-bubble series
valuesRange := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name,
series.YRange.Row1, series.YRange.Col1,
series.YRange.Row2, series.YRange.Col2,
rfAllRel, false
);
// And these are the data point labels. // And these are the data point labels.
titleAddr := GetSheetCellRangeString_ODS( titleAddr := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name, sheet.Name, sheet.Name,
series.TitleAddr.Row, series.TitleAddr.Col, series.TitleAddr.Row, series.TitleAddr.Col,
series.TitleAddr.Row, series.TitleAddr.Col, series.TitleAddr.Row, series.TitleAddr.Col,
rfAllRel, false); rfAllRel, false
);
count := series.YRange.Row2 - series.YRange.Row1 + 1; count := series.YRange.Row2 - series.YRange.Row1 + 1;
// Store the series properties // Store the series properties
@ -7186,11 +7205,17 @@ begin
'chart:class="chart:%s">' + LE, 'chart:class="chart:%s">' + LE,
[ AStyleID, valuesRange, titleAddr, CHART_TYPE_NAMES[series.ChartType] ] [ AStyleID, valuesRange, titleAddr, CHART_TYPE_NAMES[series.ChartType] ]
)); ));
if domainRange <> '' then if domainRangeY <> '' then
AppendToStream(AChartStream, Format( AppendToStream(AChartStream, Format(
indent + '<chart:domain table:cell-range-address="%s"/>' + LE, indent + '<chart:domain table:cell-range-address="%s"/>' + LE,
[ domainRange ] [ domainRangeY ]
)); ));
if domainRangeX <> '' then
AppendToStream(AChartStream, Format(
indent + '<chart:domain table:cell-range-address="%s"/>' + LE,
[ domainRangeX ]
));
AppendToStream(AChartStream, Format( AppendToStream(AChartStream, Format(
indent + ' <chart:data-point chart:repeated="%d"/>' + LE, indent + ' <chart:data-point chart:repeated="%d"/>' + LE,
[ count ] [ count ]
@ -7478,7 +7503,7 @@ begin
end; end;
Result := Format( Result := Format(
indent + ' <style:style style:name="ch%d" style:family="chart">' + LE + indent + ' <style:style style:name="ch%d" style:family="chart">', [ AStyleID ]) + LE +
indent + ' <style:chart-properties ' + indent + ' <style:chart-properties ' +
interpolationStr + interpolationStr +
verticalStr + verticalStr +
@ -7489,9 +7514,7 @@ begin
'chart:auto-size="true" ' + 'chart:auto-size="true" ' +
'chart:treat-empty-cells="leave-gap" ' + 'chart:treat-empty-cells="leave-gap" ' +
'chart:right-angled-axes="true"/>' + LE + 'chart:right-angled-axes="true"/>' + LE +
indent + ' </style:style>' + LE, indent + ' </style:style>' + LE;
[ AStyleID ]
);
end; end;
{ <style:style style:name="ch1400" style:family="chart" style:data-style-name="N0"> { <style:style style:name="ch1400" style:family="chart" style:data-style-name="N0">