proper Xaxis design lazarus / 55-TAChart ---------------------------------------------------------------------- frederic · 2015-02-10 21:25 dear specialist, i have encoutered propblems in setting up the layout of an x-axis: ultimate goal : the labels shown on the x-axis should be  time so     X:XX once  a day: a date identification      example  06/02/14 so far straigth forward " tdatetime" but  the available chart  data are available only a period of a day;  say shopping hours (9:00h  to 18:30h) and i want get chart  without the empty spaces of night hours (in future also no free days or weekends;but that is for further optimization) secondly ; i select only data  as only a section out of big file and want to run a program on this.This requires the need of having the x-axis setup during a number of days before data starts and also a number of days after the termination of the available data also continuing the describted  x-axis setup . i descided to use lineseries on the basis of x-axis numbers and making an string array covering the whole x-axis range in datetime values to provide the mark labels as text. Code: [Select] procedure TForm1.inputOKEClick(Sender: TObject); var n:integer; begin        chart1lineseries1.clear;      Fstartingday:=strtodatetime (startingdate.text);      Fopeningtime:=strtodatetime (openingtime.text);      Fclosingtime:=strtodatetime (closingtime.Text);      Fnumberofdays:=strtoint ( numberofdays.text);      FextraNODbefore:=strtoint ( extraNODbefore.text);      FextraNODafter:=strtoint ( extraNODafter.text);     //determination of size of x-axis array     XaxisMAXdefinition(Fstartingday,Fopeningtime , Fclosingtime,Fnumberofdays,FextraNODbefore,FextraNODafter,Fxaxismax ,Fgrafstartmin,Fgrafclosemin, Fminperday);     setlength(FXaxDT, Fxaxismax+1);     setlength(FXaxisstring,Fxaxismax+1);     setlength(Fxtime,Fxaxismax+1);     setlength(Fxday,Fxaxismax+1);    //determination of size of x-axis datetime arrays    Xaxisdefinition(Fstartingday,Fopeningtime , Fclosingtime,Fnumberofd ays,FextraNODbefore,FextraNODafter,FXaxDT,Fxtime,Fxday);     // making the string array        for  n:=0 to  Fxaxismax do                begin                     if n > 0 then                     begin                           if Fxday[n]<> Fxday[n-1] then  FXaxisstring[n]:= '     '+ #13#10 + datetostr(Fxday[n] );                            if Fxday[n]= Fxday[n-1] then FXaxisstring[n]:= timetostr(Fxtime [n])//only time    ;                     end;                end;        graf(Fgrafstartmin,Fgrafclosemin); end;                                   de graf procedure Code: [Select]procedure TForm1.graf(agrafstartmin, agrafclosemin:integer ); const MIN = -10;       MAX = 10; var  i,ii,N: Integer;  x,Y: Double; begin   N:= agrafclosemin-agrafstartmin;   Chart1.BottomAxis.Marks.Source := Chart1LineSeries1.ListSource;   for i:=0 to agrafstartmin- 1 do  begin  //   y:=nil;   <-----   does not work     Y:=1; // arbitrary value : but not wanted    Chart1LineSeries1.AddXY(i, Y,FXaxisstring[i]);    end;  for ii:=0 to N - 1 do  begin    x := MIN + (MAX - MIN) * ii / (N - 1);    Chart1LineSeries1.AddXY(agrafstartmin+ii, sin(x),FXaxisstring[agrafstartmin+ii]);  // test chart    end;    for i:=agrafclosemin to Fxaxismax do  begin    Chart1LineSeries1.AddXY(i, 1,FXaxisstring[i]);    end; end;                            in the attached program you see that only the transfer to time labels has been succesfull. The transfer to datelabels is far from optimal Code: [Select]can someone give tips to improve the situation? frederic ---------------------------------------------------------------------- wp · 2015-02-10 22:21 Quote from: fredericattached programThere is no attached program. Please attach only pas, lfm, lpi and lpr files, the others are not needed; in particular don't add the exe and the ppus. Pack all files into a zip which you can upload here. Please add also some sample data and maybe a sketch of what you want the axis to look like. The discussion will be much more efficient if I can compile the program. Are the data points equidistant? ---------------------------------------------------------------------- frederic · 2015-02-11 09:43 wp i saw that only the last of 4 zips was send so again frederic ---------------------------------------------------------------------- frederic · 2015-02-11 10:22 wp in the attachment a sketch of the x-axis  the data are equidistant (every minute between the shop opening and closing time frederic ---------------------------------------------------------------------- wp · 2015-02-11 12:35 Now I think I know what you mean. Here's a little demo with random data which I hope covers your specification. It was a bit tricky I have to confess... Non-linear axis The basic problem is that you want to "cut out" the time during the weekend and the shop's closed hour. This results in a non-linear axis. In TAChart, non-linear axes are handled by means of AxisTransformations; there are some pre-built ones, but we need here the TUserDefinedAxisTransform which gives most flexibility in defining the transformation by means of OnAxisToGraph and OnGraphToAxis events. To understand transformations you must know that there are two coordinate systems involved: one is the coordinate system of the numbers that appear as labels on the axes - in your case, these are the date/time values; TAChart calls this "Axis coordinates". The other coordinate system is the invisible linear coordinate system along the axes, like in an old-fashioned graph paper; it is called "Graph coordinates" by TAChart. The event OnAxisToGraph converts axis units (your date/times) to graph units in the plot, OnGraphToAxis converts in the opposite direction. The basic idea with the transformation is as follows: In a date/time number, the integer part corresponds to the days, the fractional part to the time between midnight to midnight.So, if you want to plot only data during the shop's open hours blow up the range between 9:00 ( time part = 9/24 = 0.25) and 18:30 (time part = 18.5/24 = 0.77083333) to the range between 0 and 1:t(graph) = (t(axis) - 0.25) (0.78083333 - 0.25). Similarly with the days: Just count the working days between the starting date (first date in the plot) and the current date to be converted and skip the weekends - my demo contains a function for this ("CountWorkDays")Therefore, the event handler for OnAxisToGraph can be written as: Code: [Select]{ Transforms a "date/time" number AX to invisible graph units } procedure TForm1.ChartAxisTransformations1UserDefinedAxisTransform1AxisToGraph(   AX: Double; out AT: Double); begin   AT := CountWorkdays(StartDate, AX) + (frac(AX) - StartTime)/(EndTime - StartTime); end; For the opposite direction, graph to axis, we need the inversion of CountWorkdays which I called AddWorkDays - it adds a given number of workdays to a starting date and correctly considers the weekend: Code: [Select]{ Converts a number which is given in the invisible graph units to a real   "date/time" value. } procedure TForm1.ChartAxisTransformations1UserDefinedAxisTransform1GraphToAxis(   AX: Double; out AT: Double); begin   AT := AddWorkdays(StartDate, trunc(AX)) +  frac(AX) * (EndTime - StartTime) + StartTime; end;    In order to activate the axis transform, you have to add a TChartAxisTransformation component onto the form. Right-click and add a TUserDefinedAxisTransform. Assign above methods to its events. Assign the AxisTransfor to the Marks.Transformation property of the bottom axis. And - this is what I always forget - assign the index of the bottom axis (1) to the AxisIndexX property of the series. Axis labels In principle, you could use a TDateTimeIntervalChartSource to convert the date/time numbers to strings. But his components has a life of its own, and I never understood how it is working. Therefore I propose to add a TListChartSource to which you "add" all date/time values and their string representation at which you want to have a label. This way you can also take care of the different format: the first label of a day should show date plus time, the other label only time. In the demo project, this happens in the method "CreateAxisLabels". In order to activate these labels you assign this ChartSource to the Marks.Source property of the bottom axis, and set the Marks.Style to smsLabel. In order to check whether the labelling procedure is correct I added the date/times of each datapoint to the series. You can turn this off by LineSeries.Marks.Style = smsNone. ---------------------------------------------------------------------- frederic · 2015-02-11 16:20 wp , thank you, this is indeed what i was trying to achieve it looks that ,even when there are no measuring data available, the x-axis can still be created. i am going to integrate this solution in my application  where i use multiple panes to present data simultaneously ; that is  the next challenge thank you again frederic ---------------------------------------------------------------------- frederic · 2015-02-12 21:14 Wp, I am one step further in using your decribed axis solution. First I have changed the position of the labels of the axis (from vertical to horizontal),which means  than the length of the text of the label becomes important to get consistent results . As described we will have the date indication at the first mark of that partical day . I prefer of having that date markation  at the second level of the label  for clarity . The date indication has been changed to 'dd/mm' which total width in pixels should be equal to the time indication 'hh:mm'.   What type of label font should than be used  to achieve this? (I have choosen labelfont charset  :  default_charset                            labelfont name : courier) I added to your project an X-axis zoom and pan procedure and that worked. The only problem i see there is that only the first data indication is consistent and with panning you see very often the date indication dissappear  Why is that? ps In the attached project I included some setting possibilties like number of minutes between marks,minimum pixels between  labels and the zoomfactor . I could ,however,not find a setting to avoid this dissapearing frederic ---------------------------------------------------------------------- wp · 2015-02-13 12:41 I am bit confused because you changed something with the program which moved the x axis labels below the lower edge of the chart and I can't find what you did... And next time, when you submit code, please pack all files into a single zip. This help me to create the project easier and yourself to overcome the file count limitation of the forum. Please see my modified demo again: You changed the distance of the x labels to 10 minutes - that's way too close, all labels are overlapping. You try to avoid this by using Marks.OverlapPolicy and adapting Axis.Intervals. But when you do this you lose control over positioning of the labels. When the automatic label finder algorithm decides that the first day label (the one with the date) is in the way you lose this important label - you simply cannot control it. Therefore, I propose to control label positioning by yourself. This is what the UpdateAxisLabels method is for (former CreateAxisLabels). It adds only those labels to the xAxisChartSource which you want to see. To take care of overlapping labels it contains now a local function "CalculateDelta" which estimates the label size and its positioning and adjusts the label interval "delta" such that overlaps are avoided. It is not yet perfect, maybe you can improve it by yourself and post the result here. I am not quite happy with this method because it does what TDateTimeIntervalChartSource should do, but this is somehow screwed up by the omitted data ranges. UpdateAxisLabels is called whenever something relevant to the labels occurs, i.e. after form resizing or chart zooming. Moveover, the demo contains quite complete handling of zooming/panning by means of mouse drags and mouse wheel. There is also a zoom history which allows you to go back to the previous zooming state. See the instructions at the top of the main form. And I also used the TChartNavScrollbar as a tool for quick navigation through a large dataset. ---------------------------------------------------------------------- wp · 2015-02-13 16:15 Another version of the demo which more clearly shows the individual days by means of a daily stripe pattern: Add another horizontal axisAt first make it a "top" axis to distinguish it from the bottom axis, at the end you can make it a bottom axis as well.Do NOT assign a transformation to it. The new axis is automatically linked to the bottom axis and uses the transformed values this way.In the "Intervals" of this new axis, disable all options except for aipUseNiceSteps. Set Intervals.NiceSteps to 0.1 only to get labels at 0, 1, 2, etc - these are the full days of the transformed bottom axis.When you run there should be only integer top labels only, and they should coincide with the 9:00 labels of the bottom axis.In order to create the stripe pattern, add a TAChartStyles component.Assign this component to Marks.Stripes of the top axis.Click the "..." button next to Styles in the Object Inspector. Add two Styles. They will be switched from day to day.For each style, set UsePen and UseFont to falseSet Brush.Color of the first style to some bright color (clSkyBlue) and Brush.Color of the other style to some other color (clMoneyGreen)Turn off the axis labels by Marks.Style = smsNoneMake the top axis a second bottom axis (Alignment = calBottom) ---------------------------------------------------------------------- frederic · 2015-02-16 11:52 Wp very impressive results wp. I looked into the calculatedelta function, which should rule the distance between the date time labels .By the introduction of a second bottom axis the problem of overlapping only occur by time labels of neighbouring days and  only when the distance of  the marks position of last time label of the day to the marks position of of the starting time on the next day, is smaller then  half the time label width .  However I could not find the solution yet. frederic ---------------------------------------------------------------------- frederic · 2015-02-17 09:00 update: there is a dependency with the position of opening time and closing time and the number of opening hours. The datetime_intervals - array should match with the number of opening hours: When we have a starting time at 0:01 at night and closing 1 minute before midnight then  there is absolute no overlapping. Having an opening at 9:00 h and closing at 11:00 h  then there is a permanent overlap of labels at 9:00h for my specific case I could live with the result up till now but For a more generic solution will I am going to look into this direction frederic ---------------------------------------------------------------------- frederic · 2015-02-17 11:19 it looks like the problem is solved i changed the datetime_array to the following: Code: [Select]DATETIME_INTERVALS: array[0..7] of TTime = (       1*ONE_MINUTE, 5*ONE_MINUTE,       15*ONE_MINUTE,       ONE_HOUR,  4*ONE_HOUR,       1.0, 5.0, 25.0);    The idea is that there is sufficient space for the new labels whilst zooming and unzooming . The ratio between the intervals should therefore be larger dan a factor 3-4 to get consistent results frederic ---------------------------------------------------------------------- wp · 2015-02-17 18:35 QuoteThe ratio between the intervals should therefore be larger dan a factor 3-4 to get consistent resultsNot sure what you mean here. But the label overlap in my example in which the first label is at 9:00 and the last label at 19:00 happens because the label distance is calculated for the first two labels of a day, but the last one can get much closer to the first one of the next day depending on label interval. You can avoid this overlap if you extract from my code a function "LabelsOverlap(ATime1, ATime2): Boolean" which checks the widths of these two adjacent tables and compares them with their distance on the axis. If the last label of a day overlaps with the first label of the next day you can just delete the last label of the day from the chartsource (xAxisChartSource.Delete(xAxisChartSource.Count-1)). This results in a non-linear spacing of the labels, but this looks definitely better than overlapping labels. ---------------------------------------------------------------------- frederic · 2015-02-18 09:25 wp or always skip the last time label of any day in UpdateAxislabels: Code: [Select]if DELTA < 1.0 then  begin    if dt > trunc(dt) + EndTime then begin             // skip always last time label of a day<------        xAxisChartSource.Delete(xAxisChartSource.Count-1) ;         dt := trunc(dt) + 1 + StartTime;         if trunc(dt) = EndDate then break;         format := TIME_FORMAT + LineEnding + DATE_FORMAT;         end;   end;  this is the most simple approach. what not have been solved is the the handling of free days(christmas,new year etc). I intend to make a list of dates free days per year  and test the dates in UpdateAxislabels Code: [Select]  // Skip weekends             weekday := DayOfWeek(dt);             if ((weekday in [1, 7] )  or freeday) then                begin                  dt := dt + 1;                  Continue;                end;          frederic ---------------------------------------------------------------------- wp · 2015-02-18 09:53 QuoteI intend to make a list of dates free days per year  and test the dates in UpdateAxislabelsYes - that's exactly how I would do it. But make sure that the free days are handled in the transformation procedures as well (AddWorkday, CountWorkdays), otherwise axislabels and x values will get out of sync (To check this issue assign the true date to each data point and turn on the series labels as I did in the first sample). ---------------------------------------------------------------------- frederic · 2015-02-21 14:50 wp By introducing the above method in my application it was very soon clear that this method will unfortunately not be applicable in my situation. The preveous testing was done with a random function , but also a random function in the x-axis. In order to simulate where I was looking for ,I made a sine-function based  on 1- minute data instead of this random x -function and got the attached picture. Then i realised that i have data  available on 1 minute basis during the  times that shops are open. These data are listed together in one big file so that the last minute of a day at closing time will be directly followed by the first  minute after starttime of the next working day. The line numbering of this file is,in fact ,the linear scale of the bottom axis.Because all the calculations and results are based on this line numbering so do all the derived calculations.The showing of the datetime labeling is only for presentation reasons and will be useful when grafical projections from history into the future days are being made but everything is based on the lineair linenumbering. Therefore the x-axis should be based on a linear scale( the 1 minute( or X- minute or x-days) numbering and  X-axis labeling with datetime indication should  be derived from that  . The only restriction is that datetime labels do not overlap .So no ChartAxisTransformations for the X-axis in this case. At the moment I have solved this by making use of vertical constantlines in order to indicate the separation of the day and hours and using a crosshair with datetime indication and value of the data per minute .There is bottomaxis labeling but that suffered from the label overlap and therefoore made invisible. It could be of help when the vertical constantline would have a label markation at the bottomaxis which you could fill with date or a time markation; but that is not foreseen in its properties. So all X-axis info has to ome from the crosshair The futher disadvantage is that the croshair is only showing datetime info when there is XY info is available . frederic ---------------------------------------------------------------------- wp · 2015-02-21 18:18 I don't see the problems. In my code above, the data are generated in "CreateData" at random intervals. If you have equally spaced data points in minutes then you can calculate the date/time for each data point from the data point index and the start date and shop-opening- time. Of course you have to apply the transformation to get "true" datetimes. Something like this: Code: [Select]const   MINUTES_PER_DAY = 24*60; { Converts the line number which represents the number of minutes passed since   the first line in the file, to the date/time value of this entry. It is   assumed that the first line corresponds the the date "StartDate" and the   time "ShopOpensAt".} function TForm1.ConvertLineNumberToDateTime(ALineNumber: Integer): TDateTime; var   MinutesWhileShopIsOpen: Integer;   days: Integer;   minuteOfDay: Integer; begin   MinutesWhileShopIsOpen := trunc((ShopClosesAt - ShopOpensAt) * MINUTES_PER_DAY);   days := ALineNumber div MinutesWhileShopIsOpen;   minuteOfDay := ALineNumber mod MinutesWhileShopIsOpen;   Result := AddWorkdays(StartDate, days) + minuteOfDay/MINUTES_PER_DAY + ShopOpensAt;   // AddWorkdays is in the code provided in a few posts above   // ShopOpensAt and ShowClosesAt were formerly named StartTime and EndTime, respectively. end; procedure TForm1.CreateData; const   NUM_LINES = 50000;   EPSILON = 1.0/(24*60*60);  // 1 second accuracy when comparing time values var   i: Integer;   dt: TDateTime;   y: Double;   s: String;   line: Integer;   MinutesWhileShopIsOpen: Integer; begin   StartDate := EncodeDate(2014, 1, 1);  // The first line in the file corresponds to this date   ShopOpensAt := EncodeTime(9, 0, 0, 0);  // Shop opens at this time   ShopClosesAt := EncodeTime(18, 30, 0, 0);  // Shop closes at this time   MinutesWhileShopIsOpen := trunc((ShopClosesAt - ShopOpensAt) * MINUTES_PER_DAY);   for line:=0 to NUM_LINES-1 do begin      // each line in the file advances the time by 1 minute     dt := ConvertLineNumberToDateTime(line);    // Converts line index to a "true" date/time     s := FormatDateTime('dd/mm/yy hh:nn', dt);  // Store the calculated date/time to check correctness, can be omitted if this is ok     // Which data do we plot against this date/time?     // I am selecting the number of minutes passed while the shop is open     y := line mod MinutesWhileShopIsOpen;     Chart1LineSeries1.AddXY(dt, y, s);     // if the current line corresponds to the last minute of a day then it is a good idea     // to add a break to the line series to avoid a connecting line from this     // last day value to the first value of the next day.     if SameValue(frac(dt), ShopClosesAt-ONE_MINUTE, EPSILON) then   // requires unit "Math"       Chart1LineSeries1.AddXY(dt, NaN);   end;   Chart1.Prepare;    // This is the date of the last data point. Needed for the transformations.   EndDate := trunc(dt); end; ---------------------------------------------------------------------- wp · 2015-02-21 20:39 Here is another version of the demo. It plots 50.000 data points in 1-minute intervals. It uses two x axes, each with its own ChartSource to provide the labels, a time and a date chartsource. The time labels adjust themselves such that overlapping is completely avoided - hopefully... (the last day label is removed if it overlaps with the first label of the next day). The date labels are in increments of 1 and are linked to the ChartStyles to provide a striped background per day. If you zoom out a lot the time labels are suppressed, and the date labels switch to weekly or monthly intervals. Weekly labels begin at Monday, monthly labels at the first workday of the month. Pretty universal... (There is one thing which I do not like: it is possible to scroll out of the data range using the NavScrollbar or the MouseWheel - I think I have to dig into TAChart to fix this...) Unlike my previous posts, I removed the axis transformation component now because it is easier to synchronize both axes this way. Of course, some kind of transformation is still needed to close the data gaps during night and weekend. This is done by the function ConvertDateTimeToDaysPassed when the data are inserted into the series. My sample data just plot the number of minutes passed since the first minute of each day. You can easily plug in your file data here - it is in the CreateData method. The minute counter (line counter in your file) is converted to "days passed" by means of ConvertLineNumberToDaysPassed. The integer part of these numbers is the count of full workdays since the first data point. The fractional part is the fraction of the time of the shop open hours, i.e. x.0 is at the shop opening hours, and x.99999 is immediately before the shop closes. Please study the code, I inserted a lot of comments. You must understand the principle of the transformation. And you must be familiar with basic date/time functions provided by fpc. Look at the unit DateUtils, I used several of its routines. ---------------------------------------------------------------------- frederic · 2015-02-23 11:14 Thanks wp, it looks like your latest update will overcome the problems i observed when i tried to use your code. Indeed i have to learn a lot more about tachart and  do ,in the mean time, a lot of testing to get more famiiar with it. And it is real great work  to see  what is possible with Tachart. Great respect for its developers!! So i was trying to make a crosshair for your latest code .The construction itself is not the problem but i wanted to display the values of the chartsseries in  a statusbar Up till now i used the vertical constantline X position in a chart.mouse move event as input. By using that the X position is an integer only showing days numbers ; so not of great help when you want to show time as well. The crosshairtool offers me x,y values of the chart.canvas ,but i was not able  to display the crosshair now. So how can i use the crosshair tool in this case; is there something like chart-to-canvas-operation and reverse and how to deal then when there are no series data available at the canvas minimum or maximum values of the x-axis ? frederic ---------------------------------------------------------------------- wp · 2015-02-23 14:42 There is no need to invent a crosshair tool by yourself, it is already built into the ChartTools. Double-click on the ChartTools component which is already on the form and add a DatapointCrosshairTool. If you run the program you will automatically see the crosshair tool whenever the mouse come close to a data point. If you want to modify its default behaviour you can play with its properties: DistanceMode: if you select cdmOnlyX the cursor will appear whenever the mouse is close to the x value of a data point, y is ignored. Similarly with cdmOnlyY.Shape: defines the shape of the cursor, ccsVertical, e.g. draws only a vertical line instead crossed horizontal and vertical linesSize: defines the size of the cursor: -1 mean that the cursor runs across the entire chart, otherwise it defines the length of the lines in pixels.CrosshairPen: determines the line color, line style etc of the cursor. Note that these become effective only if DrawingMode is tdmNormal.Shift: defines the mouse or key event which must occur for the cursor to show. If the set is empty as by default then the cursor shows up whenever the mouse is near a data point. If you add, e.g. ssShift, then the SHIFT key must be pressed additionally.etc.In order to show the current position of the crosshair cursor in the statusbar you hook into the OnDraw event. Maybe named a bit misleading for this purpose, this event has everything ready to get the position of the cursor. The event itself is not very verbose, but the tool's properties tell you everything. However the tool parameter passed to the event handler has to be cast to a TDatapointCrosshairTool to get access to all properties that you need: TDatapointCrosshairTool(Sender).Series is the series at which the crosshair cursor is grabbed (or nil, if the mouse is too far off from a series). A type-cast to TLineSeries is needed to get access to all properties of the series.TDataPointCrosshairTool(Sender).PointIndex is the index of the datapoint grabbed by the cursor.Since in the CreateData method data were added to the series by AddXY the data are stored in the internal ListSource of the series. Therefore you can read the x and y coordinate of the cursor location by looking up the data point in the series' ListSource. The items stored in the ListSource are pointers to TChartDataItem records which contain the x and y coordinates in the corresponding fields. Don't forget to derefence the pointer:Code: [Select] series := TLineSeries(TDatapointCrosshairTool(Sender).Series);    // of course you can also use Chart1LineSeries1 since your chart contains only 1 series...  index := TDatapointCrosshairTool(Sender).PointIndex;  x := series.ListSource[index]^.X;  y := series.ListSource[index]^.Y; From the retrieved x and y coordinates you can construct the string for the statusline. But be aware that x does not yet lead to a decent date/time string: Data were added as "days since first data point", therefore you have to calculate the date from this by inverting the ConvertDateToTimDaysPassed function.In the attachment you find working modification of the previous demo, now with datapoint crosshair tool. The tool is activated by pressing the SHIFT key. If you want to learn more about TAChart you should have a look at its documentation (http://wiki.lazarus.freepascal.org/TAChart_documentation) and the many tutorials (http://wiki.lazarus.freepascal.org/TAChart#Documentation). I am intending to write another tutorial, covering the subject of this discussion. BTW: What is the quantity which is measured regularly in 1-minute intervals and which you are plotting? ---------------------------------------------------------------------- frederic · 2015-02-23 16:12 thanks again wp QuoteWhat is the quantity which is measured regularly in 1-minute intervals .................? i can control the amount minutes of course  but roughly 10000 minutes,but i was just about of studying if summarizing in 30 or 60 minutes would be more appropiate when i want to cover a wider time frame Quote...................and which you are plotting.. what do you mean here ? when you are referring to DATETIME_INTERVALS I used  following array  DATETIME_INTERVALS: array[0..7] of TTime = (       1*ONE_MINUTE,  5*ONE_MINUTE,       15*ONE_MINUTE,       ONE_HOUR, 2*ONE_HOUR,       1.0, 7.0, 30.0);  update 24febr: in the mean time i find out that this setup has effect on the display of the data labels in some stage of zooming ; they fully disappear whilst this is not the case in your datetime_interval array ...... so this needs some further study. one question for the crosshairtool: I saw that the datetime presentatin stops when the series value stop. is it possible to show also datetimes while the Y-value of the series  =nil frederic ---------------------------------------------------------------------- frederic · 2015-02-25 09:43 wp my question about the x-axis range of the crosshair can be solved very easy. In the data presentation there a few horizontal lines which can serve as the reference chart line. I can modify the max range by setting the range wider than the  available time range of the measured and calculated data points. You can extend this a little further and prepare x-axis definition for a full year or even make it a standard option for a customized datetime axis in tachart in the future where you can set opening/closingimes and free day in a year via properties. again thanks for your assist here frederic ---------------------------------------------------------------------- wp · 2015-02-25 10:03 Sorry I did not see your edit yesterday. Quote from: frederic in the mean time i find out that this setup has effect on the display of the data labels in some stage of zooming ; they fully disappear whilst this is not the case in your datetime_interval arrayWhen exactly does this occur? Since the smallest interval used in the label finding procedure is 1 minute, labels will eventually disappear when you zoom in so strongly that the 1-minute interval is wider than the width of the chart. Of course, you can add intervals of 30 sec, 15 sec, 10 sec, 5 sec, 2 sec, 1 sec, etc., to the method. But since you mentioned a 1-minute interval of the data, I did not see this to be necessary. Quote from: fredericI saw that the datetime presentatin stops when the series value stop. is it possible to show also datetimes while the Y-value of the series  =nil If you mean that there are no labels beyond the range of the data - well, that's the way it is designed. Labels appear only if they have been added in the UpdateLabels method. So, if you need more labels, add more of them in this procedure. The axis chartsource method gives you full control over the labelling process. Quote from: fredericQuote from: wp...................and which you are plotting.. what do you mean here ?I mean: which kind of data is on the y axis? I a looking for a good example fot the tutorial. For a shop, I could imagine: Price of an article sold logged when the cashier closes, or number of customers in the shop. ---------------------------------------------------------------------- frederic · 2015-02-25 13:06 wp it all about keynumbers and financial ratios in inventory movements ,not very exiting  for a tutorial. for the presentation i looked at graphs presented at yahoo for stockmarket .There you have same problematic and people are more used to it. frederic ---------------------------------------------------------------------- wp · 2015-02-25 14:35 How can I get stock data per minute or hour from yahoo? I only know this url where the closest interval is 1 day (although this would be enough to show how to skip the weekends in the chart...) Code: [Select]const   URL_MASK = 'http://ichart.yahoo.com/table.csv?s=%s&a=%d&b=%d&c=%d&d= %d&e=%d&f=%d&g=%s&ignore=.csv';   // s: stock   // a: start month (-1), b = start day, c = start year   // d: end month (-1), e: end day, f: end year   // g: interval ("w" = week, "d" = day) ---------------------------------------------------------------------- frederic · 2015-02-25 14:55 wp QuoteWhen exactly does this occur? transition from date to introduction of time labels see attachment I tried several datetime intervals, yours appeared oke ; the others not. other frames with smaller frames are not needed. frederic ---------------------------------------------------------------------- frederic · 2015-02-25 15:54 wp I thought i  did download minute data from yahoo in the past ,but it looks that is not possible anymore? I did some testing with 1 minute files of eurostoxx50 in the past and have some 1 minute files  around november 2013 ;however per day , so you have to glue them together to get your demonstration effect it covers more than amonth these are csv files are in .prn format but there is certainly more available on the internet frederic ---------------------------------------------------------------------- wp · 2015-02-25 21:37 Quote from: frederictransition from date to introduction of time labels see attachment I tried several datetime intervals, yours appeared oke ; the others not. other frames with smaller frames are not needed. My code was intended to demonstrate the principle only, but not to cover all cases. When you change the DATETIME_INTERVALS array you must also adapt the index from which the DayChartSource begins to search. This is because both TimeChartSource and DateChartSource use the same procedure for calculating the labels. But for the DateChartSource it does not make sense to begin with the minute-intervals. Therefore, the local procedure "CalculateDelta" defines the starting index i for the best interval for both chartsources separately - both cases are distinguished by the format string passed as a parameter. Look for the line Code: [Select]i := IfThen(AFormat = DATE_FORMAT, 12, 0); In my selection of intervals, index 12 points to the 1-day interval. If you drop some intervals you have to replace this value by the index of the array element 1.0 (1 day). Or, to be more general, add a simple function which looks for the 1.0 in the array. Thanks for the hint on the stock data. I found this reference (http://www.quantshare.com/sa-426-6-ways-to-download-free-intraday- and-tick-data-for-the-us-stock-market) which seems to provide 1-minute data, but did not yet check it out. ---------------------------------------------------------------------- frederic · 2015-02-26 09:04 wp I understand it. interesting link by the way .  Very often howver you have to pay for using  the software for downloading the free data. It is not clear if that is the case here frederic ---------------------------------------------------------------------- frederic · 2015-03-09 20:14 wp in the meantime i got hold of eurostoxx50 1 minute data between 31-10-2013 and 10-12-2014; so more than a year these are data already checked on gaps and the file i can deliver has the line format : linenumber;datetime;doubledatetime;close;stoch1;stoch2; these files are big: 25 days in 7z  equals 251 kb ;12700 lines ps stoch1 and stoch2 stands for a stochastic oscillator (2 different settings) which are often showed together with index chart  in a pane setup. if you are interested in these data for your tutorial give me a sign frederic ---------------------------------------------------------------------- wp · 2015-03-09 21:24 Is it possible to download these files somewhere? I must present the readers of the tutorial a way how to get the data. And of course, it must be a free download. ---------------------------------------------------------------------- frederic · 2015-03-10 11:07 wp, i see. These data are ,in this form ,not freely available . They originate from someone who collects  intradaydata realtime from multiple sources on the internet and builds files for his private use. I needed these files also  for testing whilst I was adapting  your code in my application. as i mentioned before I wanted to use multiple panes  there but i ran into an error which i could not solve and got back to your project and adapted it to read a file and i was able to reproduce the error I followed the instructions in the panes tutorial and before the introduction of the axistransformations it was succesfull !!   see the printscreen in the attachment ( btw group=2 for all 3 axis,group=1 for bottom2 then i filled in de marks.range and the axis range of all 3 left axises and made the usemin and usemax true and put the 3 axistransformations on the chart ; set the min/max to respectively 0-1,1-2,2-10. and connected the 3 left axises to axistransformations compiling was oke; but during debugging the SIGSEGV error came up: in   tatransformations.pas line 672: Code: [Select]procedure TAutoScaleAxisTransform.ClearBounds; begin   inherited ClearBounds;   with TAutoScaleTransformData(FDrawData) do begin     FMin := SafeInfinity;          <<----------------------------------------- error     FMax := NegInfinity;     FOffset := 0.0;     FScale := 1.0;   end; end;      what could this be ? frederic ---------------------------------------------------------------------- wp · 2015-03-10 17:02 Yes, I saw the same issue when I was introducing panes in my demo (which you'll find as attachment). The debugger showed me that the SIGSEGV occured in the PrepareAxis method called immediately from FormCreate. The problem here is that the chart has not yet been drawn and therefore the axis limits are not yet known. But you can enforce this calculation by adding a Chart1.Prepare to the start of PrepareAxis. In my demo you see two series arranged in two panes. The first pane ranges between 0 and 2, the second one between 4 and 6. These numbers are specified in the Range of the AutoscaleAxisTransform and mean that the internal y axis runs between 0 and 6, the lower third (0..2) is covered by the first series, the medium third (2-4) is empty, and the upper third (4-6) is occupied by the other series. If you want to restrict axis labels to the part of the corresponding series set the axis Marks.AtDataOnly to true. And set the title's PositionOnMarks to roughly center the axis title with respect to the corresponding series data (but centering is not very good, though...). There is also a zero-line for the upper series (a TConstantLineSeries). I did not assign an axis to it therefore you have to set its Position to 4 (that's the internal axis value where the upper pane starts) - just to confuse you  ... Play with this demo, modify properties, see what happens and try to understand why. ---------------------------------------------------------------------- frederic · 2015-03-11 09:14 wp , of course  , i understand now. you introduced the constantline as separation line between the panes . that is a good idea to provide clarity between the panes . btw I saw that the crosshair tool needs a maximum Xaxis limitation  in order to avoid a SIGSEGV error thanks frederic ---------------------------------------------------------------------- frederic · 2015-03-17 15:19 wp Two questions i see that some where i introduced  a mistake . In the attachment of the printscreen you see that the date indication and the stripes are not at the right place. I cannot find the cause for it. studying your code i spotted a line in the calculation of the delta which i don't understand . please explain Code: [Select] ...........  while DateOf(dt) <= EndDate do begin       // Skip weekends       weekday := DayOfWeek(dt);       //here will come a statement needed for check of extra free days       if weekday in [wdSunday, wdSaturday] then begin         dt := dt + 1;         Continue;       end;       // Add date+time to axis chartsource formatted according to "format"       days := ConvertDateTimeToDaysPassed(dt);       s := IfThen(NoLabels, '', FormatDateTime(format, dt));       AChartSource.Add(days, days, s);//   <<--   what is happening here PLEASE EXPLAIN       // next label       prev_dt := dt;       if DELTA < 30 then begin         dt := dt + DELTA;         if DELTA < 1.0 then begin           if dt > DateOf(dt) + ShopClosesAt then begin             dt := DateOf(dt) + 1 + ShopOpensAt;             // Remove the last label of a day if it overlaps with the first label             // of the next day             if LabelsOverlap(prev_dt, dt, format) then               AChartSource.Item[AChartSource.Count-1]^.Text := '';           end;         end; ---------------------------------------------------------------------- wp · 2015-03-17 16:36 Unfortunately you do not show all your code. The stripes are offset because they seem to have a different starting point. You must make sure that the datasources of both axes start at the same time. Maybe the first datapoint of one chartsource starts at, say, 9:00 and the other source starts at midnight. If both series go through the axis transformation then the 9:00 data point becomes 0 while the midnight data point becomes some negative fraction. Be caseful to apply the transformation correctly. To be more precise: I'd speculate that the first datapoint of the 9:00 series is "startdate + startime" (where starttime corresponds to 9:00), but the midnight data start at "startdate" only. The "AChartSource.Add(days, days, s)" adds the number of days passed since the 1st data point ("days") as an x and y value to the chart source, along with the label "s". Using the days also for the y value, is not necessary, therefore, you can use "AChartSource.Add(days, 0, s)" (with 0 or any other value for y) as well. But maybe you want to rotate the chart (interchange x and y) sometimes in the future, hence I am inserting the value for y as well, and it does not cost anything. - It has nothing to do with the offset in time scales. ---------------------------------------------------------------------- frederic · 2015-03-17 20:16 wp QuoteTo be more precise: I'd speculate that the first datapoint of the 9:00 series is "startdate + startime" (where starttime corresponds to 9:00), but the midnight data start at "startdate" only. your analysis was completely right!  startdate always starts at 0:00 hours for both bottomaxises. i did set the datetime of the first line of the file (starting at 9:01 hours) to be equal with startdate; and that is wrong. so solved. The second question: I now see that the y-value "days" was an arbritrary value; oke ,that solved it!! thanks again frederic