Lazarus

Programming => Graphics and Multimedia => TAChart => Topic started by: barsoom on October 14, 2019, 04:00:19 pm

Title: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on October 14, 2019, 04:00:19 pm
Hi forum,
I have an stringgrid with data, and i would like to plot them in a chart. I don´t know the number of rows and columns on design-time, so I am coding this to add series and to send the data to each serie:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotValuesExecute(Sender: TObject);
  2. var
  3.   Count1: Integer;
  4.   Count2: Long;
  5.   j: Long;
  6.   FLine : TLineSeries;
  7. begin
  8.   with StringGrid1 do
  9.   j := Rowcount;
  10.   begin
  11.     for Count1 := 1 to ColCount-1 do
  12.     begin
  13.       FLine := TLineSeries.Create(Chart1);
  14.       Chart1.AddSeries(FLine);
  15.       Chart1.Series.Items[Count1].Name:= StringGrid1.Cells[i,0];
  16.       for Count2 := 1 to RowCount-1 do
  17.         begin
  18.            Chart1.Series[Count1].AddXY(strtofloat(Cells[0,Count2]),strtofloat(Cells[1,Count2]), '' , clBlue );
  19.         end;
  20.     end;
  21.   end;
  22. end;
I know the code is still not good, but the problem is in AddXY... i saw many examples on the lazarus wiki and in this forum, but this is the error i have when i try to compile:
Quote
Error: identifier idents no member "AddXY"

May be you could help me with this issue. Thanks!
Title: Re: Populating a Chart from StringGrid. AddXY problem
Post by: wp on October 14, 2019, 04:57:45 pm
This is because you call AddXY for Chart1.Series.Items[ i] -- but this is a TBasicChartSeries which has only the most fundamental properties and methods. The AddXY method is introduced in the class TChartSeries from which TLineSeries is derived. (See the overview of the TAChart series classes in the wiki documentation: https://wiki.lazarus.freepascal.org/TAChart_documentation#Series). Since you created Chart1.Series.Items[ i] as TLineSeries you must type-cast it to TLineSeries in order to have the TLineSeries properties available:

Code: Pascal  [Select][+][-]
  1.   (Chart1.Series[index] as TLineSeries).AddXY(some_x, some_y, some_label, some_color);

Or, since you stored the pointer to the created series in variable FLine which is declared as TLineSeries you can call AddXY immediately, without type-cast:

Code: Pascal  [Select][+][-]
  1.   FLine.AddXY(some_x, some_y, some_label, some_color);

A few notes on issues in your code:
(1) Assuming that the chart is empty when you call TForm1.PlotvaluesExecute the first created series gets the index 0 in the series list of the chart. But you call the AddXY for the series with Index Count1 which starts with the value 1 in the for loop. Therefore, your code will crash here because Chart1.Series[1] does not yet exist.

(2) You assign the caption of column i to the "Name" property of the series. This works, but probably is not what you want -- you probably want to define the text with which the series is identified in the legend. The corresponding property is called "Title".

(3) Since all data points get the same color, clBue, there is no need that you specifiy the color in the 4th parameter of the AddXY call. It is simpler to set the "SeriesColor" property globally for all data points of this series.

(4) After the "with StringGrid1 do" you begin a block within which you access StringGrid1 properties. But you insert another line between the "with" line and the following "begin" -- this has the effect that the "with" applies only to this line -- all what is inside the begin/end does not belong to the StringGrid any more!. Consequently, ColCount, RowCount and other identifiers are no longer understood. To avod this put the "j := RowCount" after the "begin" - but I don't see that it is used anyway, so better skip it altogether.

So, in total this is the code that I would use (not tested, some typos can be around here and there):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotValuesExecute(Sender: TObject);
  2. var
  3.   Count1: Integer;
  4.   Count2: Integer;  // Long <-- In Pascal there is no "Long" type, use "LongInt" instead; usually "Integer" is enough.
  5. //  j: Long;  <---- not needed.
  6.   FLine : TLineSeries;
  7. begin
  8.   with StringGrid1 do
  9.   begin
  10. //    j := RowCount;  // This is not used anywhere    
  11.     for Count1 := 1 to ColCount-1 do
  12.     begin
  13.       FLine := TLineSeries.Create(Chart1);
  14.       Chart1.AddSeries(FLine);
  15.       FLine.Title := StringGrid1.Cells[i,0];
  16.       FLine.SeriesColor := clBlue;
  17.       for Count2 := 1 to RowCount-1 do
  18.       begin
  19.         FLine.AddXY(strtofloat(Cells[0,Count2]), strtofloat(Cells[1,Count2]));
  20.       end;
  21.     end;
  22.   end;
  23. end;
Title: Re: Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on October 14, 2019, 06:14:15 pm
Thanks @wp for your help. yes, my code was still so dirty.
I will try your solution!
Thanks you!
Title: Re: Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on October 16, 2019, 12:56:50 pm
Thanks so much! It works good!
I also found other topic that shows exactly what i try to do:
https://forum.lazarus.freepascal.org/index.php/topic,25520.msg155099.html#msg155099 (https://forum.lazarus.freepascal.org/index.php/topic,25520.msg155099.html#msg155099)

However, i have to problems.
1) I am not able to avoid null data... in my case, the first column of the stringgrid contains date/time values, and the others the Y values. Not all the Y cells have values, and when i plot the data by the next code, the chart show a continuous line, not disconnected on the dates where there is not Y values in the stringgrid.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotValuesExecute(Sender: TObject);
  2. var
  3.   colnumber: Integer;
  4.   rownumber: integer;
  5.   ls: Tlineseries;
  6.   x: TDateTime;
  7.   y: double;
  8.   i, n: integer;
  9.   clr: TColor;
  10. begin
  11.   Chart1.ClearSeries;
  12.   colnumber := StringGrid1.ColCount;
  13.   rownumber := StringGrid1.RowCount;
  14.   for i := 1 to colnumber-1 do
  15.     begin
  16.       ls := Tlineseries.Create(Chart1);
  17.       chart1.addSeries(ls);
  18.       ls.Legend.Format := StringGrid1.Cells[i, 0];
  19.       //ls.SeriesColor:=ChartStyles1.Styles.Style[i];
  20.       clr := RGBToColor(Random(256), Random(256), Random(256));
  21.       for n := 1 to Rownumber-1 do
  22.         begin
  23.           if TryStrToFloat(StringGrid1.Cells[i, n], y) then
  24.             begin
  25.               x := StrToDateTime(StringGrid1.Cells[0, n]);
  26.               ls.AddXY(x, y, '', clr);
  27.             end;
  28.         end;
  29.     end;
  30. end;  


2) the second problem is how to assign a different color to each line serie. The code i used here is not working, and all of the series are showed in black, in spite of the use of "clr" parameter in the AddXY function.


Thanks!
Title: Re: Populating a Chart from StringGrid. AddXY problem
Post by: wp on October 16, 2019, 02:54:06 pm
1) I am not able to avoid null data... in my case, the first column of the stringgrid contains date/time values, and the others the Y values. Not all the Y cells have values, and when i plot the data by the next code, the chart show a continuous line, not disconnected on the dates where there is not Y values in the stringgrid.
This is because you nowhere use a NaN for y. Try this instead
Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotValuesExecute(Sender: TObject);
  2. var
  3.   colnumber: Integer;
  4.   rownumber: integer;
  5.   ls: Tlineseries;
  6.   x: TDateTime;
  7.   y: double;
  8.   i, n: integer;
  9.   clr: TColor;
  10. begin
  11.   Chart1.ClearSeries;
  12.   colnumber := StringGrid1.ColCount;
  13.   rownumber := StringGrid1.RowCount;
  14.   for i := 1 to colnumber-1 do
  15.     begin
  16.       ls := Tlineseries.Create(Chart1);
  17.       chart1.addSeries(ls);
  18.       // ls.Legend.Format := StringGrid1.Cells[i, 0];  <--- what is this supposed to do?
  19.       ls.SeriesColor:= RGBToColor(Random(256), Random(256), Random(256));
  20.       for n := 1 to Rownumber-1 do
  21.         begin
  22.           if TryStrToFloat(StringGrid1.Cells[0, n], x) then
  23.           begin
  24.             if not TryStrToFloat(StringGrid1.Cells[i, n], y) then
  25.               y := NaN;
  26.             ls.AddXY(x, y) ;
  27.           end;
  28.         end;
  29.     end;
  30. end;  
(I am not sure if the begin/end pairs are matching in this modified code, I am not used to your indentation style).

2) the second problem is how to assign a different color to each line serie. The code i used here is not working, and all of the series are showed in black, in spite of the use of "clr" parameter in the AddXY function.
Normally you define the color of a lineseries by setting the "SeriesColor" property or the "LinePen.Color". The individual datapoint colors set in the AddXY call are meant for the data point symbols usually (In Laz trunk you can also colorize the line segment before/after a data point). The needed change is already contained in above code.
Title: Re: Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on October 16, 2019, 04:23:27 pm
Thanks so much @wp, i had to made some modifications, and now it works nicely! Thanks!

Here is the code, in case it could be useful for others:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotValuesExecute(Sender: TObject);
  2. var
  3.   colnumber: Integer;
  4.   rownumber: integer;
  5.   ls: Tlineseries;
  6.   x: TDateTime;
  7.   y: double;
  8.   i, n: integer;
  9. begin
  10.   Chart1.ClearSeries;
  11.   colnumber := StringGrid1.ColCount;
  12.   rownumber := StringGrid1.RowCount;
  13.   for i := 1 to colnumber-1 do
  14.     begin
  15.       ls := Tlineseries.Create(Chart1);
  16.       chart1.addSeries(ls);
  17.       ls.Title := StringGrid1.Cells[i, 0];
  18.       ls.SeriesColor:= RGBToColor(Random(256), Random(256), Random(256));
  19.       for n := 1 to Rownumber-1 do
  20.         begin
  21.           if TryStrToDateTime(StringGrid1.Cells[0, n], x) then
  22.           begin
  23.             if not TryStrToFloat(StringGrid1.Cells[i, n], y) then
  24.               y := NaN;
  25.           end;
  26.           ls.AddXY(x, y) ;
  27.         end;
  28.     end;
  29. end;
An important issue. It is necessary to add "Math" to Uses section, otherwhise "NaN" returns an error.

Quote
ls.Legend.Format := StringGrid1.Cells[i, 0];  <--- what is this supposed to do?
In the mean time i readed other post from you where you explained other way to do it: to assign the series name based on the first row of the column. Now i changed to:
Code: Pascal  [Select][+][-]
  1. ls.Title := StringGrid1.Cells[i, 0];

Thanks a lot!

Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 04, 2019, 08:06:52 pm
Although it is working, now i have a problem trying to set the pointer style to circles on runtime.
It should be as simple as to add this code
Code: Pascal  [Select][+][-]
  1. ls.Pointer.Style. := psCircle;
between lines 18 and 19 in the previous post, but it returns and error on compilation:
Quote
unit1.pas(164,28) Error: Identifier not found "psCircle"

What am i doing wrong?
Thanks
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: winni on November 04, 2019, 10:56:35 pm
Don't forget the types:

Code: Pascal  [Select][+][-]
  1.  uses  ......., tatypes;

Winni

Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 04, 2019, 11:19:01 pm
When you learn how to navigate in source code you can resolve such issues easily. Click on the "Style" of "ls.Pointer.Style" and hold the CTRL key down. This opens the unit in which the Pointer.Style is declared; in this case you'll jump to the line
Code: Pascal  [Select][+][-]
  1.   property Style: TSeriesPointerStyle read FStyle write SetStyle default psRectangle;
Now repeat this with the word "TSeriesPointerStyle" and the IDE will jump to
Code: Pascal  [Select][+][-]
  1.   TSeriesPointerStyle = (
  2.     psNone, psRectangle, psCircle, psCross, psDiagCross, psStar,
  3.     psLowBracket, psHighBracket, psLeftBracket, psRightBracket, psDiamond,
  4.     psTriangle, psLeftTriangle, psRightTriangle, psVertBar, psHorBar, psPoint,
  5.     psDownTriangle, psHexagon, psFullStar);
Here we are... In the tab of the active editor page you'll see "TATypes" -- this is the unit in which the SeriesPointerStyles are declared.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 05, 2019, 10:12:36 am
Thanks @WP,
I didn´t say, but i already added "TAStyles" to the uses section, but it returned the same error. Why? Who knows? Anyway, I tried today in a different computer and it worked!
this was my problem, that i was sure it was not caused because the absence of "TAStyles" at the head of the file... because this was one of the first things i did...
i decided to uninstall lazarus and install it again in the computer where i had the problems to see what is going on.
Thanks so much anyway for the tips!!
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 05, 2019, 07:38:06 pm
I don't understand. TAStyles? This was not mentioned anywhere in this thread... Do you confuse it with TATypes which winni and I were mentioning in the previous commits?
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 07, 2019, 04:46:37 pm
You are right, sorry "TATypes". My bad.
Thanks a lot.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 13, 2019, 05:28:19 pm
I observed my program, that is really simple (to fill a stringgrid from a csv file and plot a chart based on the data), is quite slow on showing, zooming and panning the chart, but the file is quite small (1.5 Mb). May be it is better to fill a chartsource from the csv file, and to connect the chart with the chartsource. What do you think?
In that case, how could i populate a list chart course from the csv considering multiple Y data for the same X in this way:
Quote
Date&Time;Y1; Y2;Y3,(...)

Edited:
Moreover, to be able to apply other tools such as derivative plotting, or smooth, or... it is necessary to call a chartsource (listchartsource, calculatedchartsource,...). Then, i think it is better to store the data into a listchartsource better than to plot directly from the stringgrid. Am i right?

Thanks!
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 13, 2019, 08:00:43 pm
Yes, using an external ChartSource has a lot of advantages - you mentioned some of them. I recommend calling the series' Add* methods only for simple and "quick-and-dirty" programs.

You mention a csv file as your data source. What do you do with the data? Just plotting? Then a TListChartSource is fine. But sometimes the internal chart data structure is overkill - you probably do not need the individual data point colors, or the text label attached. Or you want to avoid double storage in some dedicated datastructure and in the chart source. I often use a TUserDefinedChartSource which allows me to store the data in any data structure that fits to your intentions, such as an array, a list or whatever (see tutorial https://wiki.lazarus.freepascal.org/TAChart_Tutorial:_Userdefined_ChartSource).

When you stick to a ListSource you should call the source method BeginUpdate before and EndUpdate after adding data points. This avoids a lot of internal notifications and paint operations. You'll notice a significant speed boost this way. Another options might be to disconnect the ChartSource from the series before (Series.Source := nil), and reconnecting it after adding the data (Series.Source := ListChartSource).

Of course speed depends also on how you read the csv file.

I cannot imagine a reason why zooming and panning become slow. You should try to assemble a demo to show me the issue.

In order to use several y values for the same x value you simply set the YCount of the chart source accordingly. Note however, that determining colors and legend texts of the "sub-series" is a bit more complicated. You must use ChartStyles and add a style for each "sub-series". Working with ChartStyles is explained in this tutorial: https://wiki.lazarus.freepascal.org/TAChart_Tutorial:_Stacked_BarSeries.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 15, 2019, 01:25:03 pm
Thanks.
Yes, i use a csv file. At present time i just only want to plot the data, and explore them (zoom, panning)... To do it with excell is a pain... So, in this way i hope to be able to see de data in detail, measure distances, and to export plots.

I attached a simple example of a program and a data file. There is only o button, so, no doubts about how it works. The program load the data into a tringgrid (tab3), and then plot the data in the chart (tab 2).
In this example there are not the charttools to zoom and panning using the mouse wheell and the control and alt button on keyboard. After to remove all the tools, the simple program is still so slow when zooming. this is why i asked about to load the data on a ListChartSource better than on a stringgrid... may be it increase the speed.

The example file is 1.4Mb, so, it is available here:
https://www.dropbox.com/s/81ysd1x5x1a21yi/example_data.csv?dl=0

Thanks!

Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 15, 2019, 01:42:54 pm
OMG! :o
I just discovered the cause of the slow behavior! It is the navigation panel! If you hide it (it is not required to disconnect it from the chart), just only to hide it.
Now the program is pretty fast when zooming and pannning!

However, i need to see the navigation pannel to know where i am when zooming in the plot!  >:D  So, i have a problem... may be it could be a different topic for this forum.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 15, 2019, 05:49:15 pm
Testing your demo I do not see a speed improvement when the NavPanel is gone.

The issue is something very simple, and we had this topic several times recently in the forum: when the Canvas.Pen.Width is >1 a severe speed loss is observed in Windows (due to "cosmetic" and "geometric" pen style optimizations in the API). While not noticable with "normal-sized" plots it becomes very dramatic with large datasets as in your case. So, just setting "ls.LinePen.Width := 1" in AcPlotFromGridExecute does the job.

You can also speed up populating the chart series when you put a "ls.ListSource.BeginUpdate" and "ls.ListSource.EndUpdate" around the "i" loop in AcPlotFromGridExecute. With this change the file reads in 0.46 s instead of 1.21 s.

To make the data file readable also in other countries (I could not read your data file at first) you should also take care of the DateSeparator and the year-month-day order in ShortDateFormat. Instead of touching DefaultFormatSettings I usually introduce a local TFormatSettings variable which defaults to DefaultFormatSettings and changes the elements needed, and is passed to the string conversion function as last parameter. This way the format settings of the OS are retained and the user can work with his standard format settings.

In order to visualize the zoomed range within the entire data range you can also use a TChartNavScrollBar, instead of the TChartNavPanel.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.AcPlotfromGridExecute(Sender: TObject);
  2. var
  3.   colnumber: Integer;
  4.   rownumber: integer;
  5.   ls: Tlineseries;
  6.   x: TDateTime;
  7.   y: double;
  8.   i, n: integer;
  9.   fs: TFormatSettings;    // <------ added
  10. begin
  11.   // <---- added
  12.   fs := DefaultFormatSettings;
  13.   fs.DecimalSeparator := '.';
  14.   fs.DateSeparator := '/';
  15.   fs.ShortDateFormat := 'dd/mm/yyyy';
  16.   // ------- end addition ------>
  17.  
  18.   colnumber := StringGrid1.ColCount;
  19.   rownumber := StringGrid1.RowCount;
  20.  
  21.   for i := 1 to colnumber-1 do
  22.     begin
  23.       ls := Tlineseries.Create(Chart1);
  24.       chart1.addSeries(ls);
  25.       ls.Title := StringGrid1.Cells[i, 0];
  26.       ls.SeriesColor:= RGBToColor(Random(256), Random(256), Random(256));
  27.       ls.LinePen.Width := 1;   // <--- changed from 2 to 1.
  28.       ls.Pointer.Style := psCircle;
  29.       ls.ShowPoints:= true;
  30.       ls.Pointer.Visible := false;            // <----------- This reverts the previous line. ShowPoints and Pointer.Visible are the same!
  31.       //ls.Marks.Style:= smsLabel;
  32.       ls.Marks.Visible := False;
  33.       ls.ZPosition := colnumber-1-i;
  34.       ls.ListSource.BeginUpdate;      // <-------- added
  35.       for n := 1 to Rownumber-1 do
  36.         begin
  37.           if TryStrToDateTime(StringGrid1.Cells[0, n], x, fs) then
  38.           begin
  39.             if not TryStrToFloat(StringGrid1.Cells[i, n], y, fs) then
  40.               y := NaN;
  41.           end;
  42.           ls.AddXY(x, y);
  43.         end;
  44.       ls.ListSource.EndUpdate;    // <-------- added
  45.     end;
  46. end;

To avoid double storage of values as floating point numbers in the chart's source and as strings in the grid you could introduce an array or list of records (or objects) resembling the native data and use a TUserDefinedChartSource to pass these data to the series. And you could use a TDrawGrid instead of a TStringGrid to display the data values; this will also speed up populating the grid dramatically.

I am attaching a modified version of your demo in which a UserDefinedChartSource and a DrawGrid are used.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 18, 2019, 12:09:11 pm
Thanks so much @wp!

yes, the linePen.Width is also an issue... Value 1 is quite small and not in all cases (may be colors), the plots looks nice. However, i have on mind to allow to change it on run time, so, not a big problem.

About the Navigation section, i will explore th chartnacscrollbar as you propose.

Great idea the TFormatSettings variable. I think i will create a form to allow to do changes on this settings

Thank you so much for your code. It works nicely. There is only an small issue. It does not change the entire data... It miss the last column of data. In the example file there are 8 columns (datetime  and 7 temperatures), but only 7 are loaded inthe drawgrid, and in the chart.

I think the problem is in LoadfromCSVFile procedure, but i do not see where is the problem.

Thanks!


Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 18, 2019, 01:28:44 pm
I think the problem is in LoadfromCSVFile procedure, but i do not see where is the problem.
This is because stringhelper "Split()" in FPC 3.04 has the issue that it does not add an empty string as last entry when the split string ends with a separator. I think this was fixed recently in fpc trunk.

You can take care of it when you set the length of the Temperatures array of each data point by using the  FTitles array where every temperature column has an entry:
Code: Pascal  [Select][+][-]
  1.         SetLength(FData[n].Temperatures, Length(FTitles));  // instead of ... Length(sa)-1)
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 18, 2019, 03:30:36 pm
Much better! Thanks. The issue now is that it fills this last column with "0,000" when there is not data, not keeping it empty like in the other columns. It results on cero values also in the plot and not "NaN"
Thank you!

Edited:
There is another issue: It does not assign the symbol to the first creater series in the chart:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.PlotData;
  2. var
  3.   i: integer;
  4.   //ls: TLineseries;
  5.   ucs: TUserDefinedChartSource;
  6. begin
  7.   Chart1.ClearSeries;
  8.   if Length(FData) = 0 then
  9.     exit;
  10.  
  11.   for i := 0 to Length(FData[0].Temperatures) - 1 do // <-- It starts here to crease new line series for each temperature column.
  12.   begin
  13.     ucs := TUserDefinedChartSource.Create(self);
  14.     ucs.PointsNumber := Length(FData);
  15.     ucs.OnGetChartDataItem := @GetSeriesData;
  16.  
  17.     ls := TLineSeries.Create(Chart1);
  18.     ls.Title := FTitles[i];
  19.     ls.SeriesColor := RGBToColor(Random(256), Random(256), Random(256));
  20.     ls.LinePen.Width := 1;
  21.     ls.Pointer.Style := psCircle;  //  <-- It seems this does not affect to the first created serie.
  22.     ls.ShowPoints := true;
  23.     ls.Marks.Visible := false;
  24.     ls.Source := ucs;
  25.     ls.Tag := i;
  26.     ls.ZPosition := i;
  27.     ucs.Tag := PtrInt(ls);
  28.     Chart1.AddSeries(ls);
  29.   end;
  30. end;      

After to load the data, all the series shows points, except the first one... But it is created, so the loop is working properly for the data reading...
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: wp on November 18, 2019, 06:29:41 pm
The issue now is that it fills this last column with "0,000" when there is not data, not keeping it empty like in the other columns. It results on cero values also in the plot and not "NaN"
This was a quickly written example without much testing. You just must get the indexes right. It is only a bit complicated because the Length of the string array sa[] may be shorter than that of the FTitles array.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.LoadFromCSVFile(AFileName: String);
  2. var
  3.   L: TStrings;
  4.   sa: TStringArray;
  5.   n: Integer;
  6.   fs: TFormatSettings;
  7.   y: Double;
  8.   i, j, k: Integer;
  9.   t: TDateTime;
  10. begin
  11.   t := Now;
  12.  
  13.   fs := DefaultFormatSettings;
  14.   fs.DecimalSeparator := '.';
  15.   fs.DateSeparator := '/';
  16.   fs.ShortDateFormat := 'dd/mm/yyyy';
  17.  
  18.   L := TStringList.Create;
  19.   try
  20.     L.LoadFromFile(AFileName);
  21.     SetLength(FData, L.Count-1);
  22.  
  23.     sa := L[0].Split(';');
  24.     SetLength(FTitles, Length(sa) - 1);
  25.     for i := 1 to High(sa) do
  26.       FTitles[i-1] := sa[i];
  27.  
  28.     n := 0;
  29.     for i := 1 to L.Count - 1 do
  30.     begin
  31.       sa := L[i].Split(';');
  32.       if sa[0] = '' then
  33.         Continue;
  34.       if TryStrToDateTime(sa[0], FData[n].DateTime, fs) then
  35.       begin
  36.         SetLength(FData[n].Temperatures, Length(FTitles));
  37.         for j := 0 to High(FTitles) do
  38.         begin
  39.           k := j+1;  
  40.           if (k < Length(sa)) and TryStrToFloat(sa[k], y, fs) then
  41.             FData[n].Temperatures[j] := y
  42.           else
  43.             FData[n].Temperatures[j] := NaN;
  44.         end;
  45.         inc(n);      // count valid lines; adjust array length at end
  46.       end;
  47.     end;
  48.     SetLength(FData, n);  // Set array length to count of lines with date
  49.   finally
  50.     L.Free;
  51.   end;
  52. end;

There is another issue: It does not assign the symbol to the first creater series in the chart
[...]
After to load the data, all the series shows points, except the first one... But it is created, so the loop is working properly for the data reading...
Maybe just a consequence of the index error in LoadFromCSVFile.
Title: Re: [SOLVED] Populating a Chart from StringGrid. AddXY problem
Post by: barsoom on November 18, 2019, 08:27:59 pm
Perfect!!!

The problem with values on empty cells is solved with your new code. Thanks a lot!!!

About the problem with visible points... forget it. It was caused by my fault. I accidentally changed a number in another piece of code in other part of the program.

So, definetively SOLVED.

Thanks a lot @wp!!
TinyPortal © 2005-2018