Introduction
I am using the TChart component and am finding the options rather confusing and hard to find what I need.
The Left Axis of the chart has a minimum of 0 and a maximum of 5000, the Bottom Axis has a minimum of 0 and a maximum of 52 (weeks in a year).
In my chart I have 2 series that are populated based on values from a record I have created which can be edited at runtime, everything is working good so far.
Problem
Take a look at the below image of part of the chart filled with some random values:
I am not sure if such an option exists or not, but notice from week 4 onwards where no values have been added there is a solid red line.
I don't want the chart to plot values that are less than 0, so in this example from week 4 onwards there should be no more lines (I can confirm that from week 4 onwards I have the values set at -1)
Week 1 to 4 does not show this bottom red line, obviously because the values are greater than 0 and are therefore plotted above the line, but this should make my problem a bit more clearer.
So, how do I prevent the chart from plotting / drawing values that are less than 0, importantly for the Bottom Axis of the chart?
I am sure there must be a simple option somewhere, I just cannot find it if there is one as I am not too familiar with the TChart Component.
You have the possibility when adding values to a series to add them as null.
This will prevent them from showing.
Ken is correct saying that a tricky situation is to handle a single value surrounded by null values.
Picking another series type than FastLine and show dots as well as lines might work better.
For a FastLine series to show gaps, set:
Series1.IgnoreNulls := False;
And to discriminate values below zero:
if (y < 0) then
Series1.AddNullXY(x, y, '')
else
Series1.AddXY(x, y, '');
if y < 0 then
LineSeries1.AddXY(x, y, '', clNone)
else
LineSeries1.AddXY(x, y, '', clRed);
Related
I have a working time history plot that removes one data point as another one is added after two minutes of data has accumulated. The trouble is that after say 10 minutes it is fairly difficult to see the duration of the data set being displayed (even using tickInterval). Is there a way to display fixed x axis labels after two minutes of data has accumulated? Specifically, I'm looking for truly fixed labels that do not scroll to the left with the data.
I tried using setCategories:
chart.xAxis[0].setCategories(cats, true);
Where cats is an array with sacrificial values prepended that will be removed during the shift, but the x axis labels still scroll left and I don't want that.
Thanks.
Edit:
I guess the way I previously described what I wanted to display on the x axis is not ideal. A better way to display this time (x) axis would be to have -120 on the far left and 0 on the far right. This would still require a non-scrolling x axis where the left most data point is over -120 and the right most data point is over 0.
If I'm following you, you want to shift off the start of the series, but leave the xAxis alone?
You can just set an xAxis min:
xAxis:{
min: 0
}
See example.
EDITS
In response to your comment. I think the easiest thing to do would be to shift the data yourself and then use setData to redraw the series.
newData = [];
var seriesData = chart.series[0].data;
// shift the data
for (var i = 0; i < (seriesData.length - 1); i++){
newData.push({x: seriesData[i].x, y: seriesData[i+1].y});
}
// new point for last
newData.push({x: seriesData[seriesData.length - 1].x,
y: Math.random() * 100});
chart.series[0].setData(newData, true);
Updated fiddle.
Linear Dataseries Say all y axis is 0, Autoscaling on y axis is not working as desired.
High stock version - 1.3.1
DataSeries to be plotted,
[[1149724800000,0],[1149811200000,0],[1150070400000,0],[1150156800000,0],[1150243200000,0],[1150329600000,0],[1150416000000,0],[1150675200000,0],[1150761600000,0],[1150848000000,0],[1150934400000,0],[1151020800000,0],[1151280000000,0],[1151366400000,0],[1151452800000,0],[1151539200000,0],[1151625600000,0]]
OR
[[1149724800000,20],[1149811200000,20],[1150070400000,20],[1150156800000,20],[1150243200000,20],[1150329600000,20],[1150416000000,20],[1150675200000,20],[1150761600000,20],[1150848000000,20],[1150934400000,20],[1151020800000,20],[1151280000000,20],[1151366400000,20],[1151452800000,20],[1151539200000,20],[1151625600000,20]]
Try Above series with Y min set to 0 and Y Min to Auto.
I had the same issue with 0 values displayed in the center.
The only way I found to get round it was to either use tickPositions and pass range values across to it for the data based of min/max values and ones in between or to use the tickPositioner call back function to return to go through the data and return the tick values based on these.
Still fairly new to highcharts so there is probably another way I don't know of. If anyone knows how to have 0 values displayed at the bottom of the chart while it auto-scales then please post as I would love to know how/if its possible.
Edit: turns out this isn't really possible currently, only way to get around it is to set min/max values and that won't allow auto scaling but at least it stop there from being any middle 0 values.
I’ve got my TeeChart looking pretty good, the grid lines / ticks appear where I wish and so forth (the X Axis LabelStyle is set to talAuto). Now I need to format the X Axis labels so they display a time offset rather than the data point number.
I’ve changed the LabelStyle to talMark and am handling the chart’s OnGetAxisLabel. The problem is that my event handler is called for every X Axis point rather than just the points which displayed labels when I had LabelStyle set to talAuto.
How can I set things up so my event handler is only called for the labels which were drawn when LabelStyle was talAuto?
I’m using C++ Builder XE3 and the version of TeeChart included with IDE.
UPDATE inspired by Yeray's comment:
With LabelStyle set to talAuto I always get ValueIndex of -1 passed to my event handler. In looking at the LabelText, I see values that I would have expected in ValueIndex, but I also get an equal number of incorrect values (ignoring non-X Axis values). For example...
My chart displays 5 X-Axis values: 200, 400, 600, 800, 1000. My event handler is called 10 times, in this order:
ValueIndex LabelText
-1 937.5
-1 0
-1 240,000
-1 200
-1 239,800
-1 1,000
-1 800
-1 600
-1 400
-1 200
I have no idea where the first 5 values came from or why my event handler was called.
NOTE: I removed the C++ Builder tag because this question might be answered by anyone using TeeChart regardless of their language; the fact that I'm using BCB is not important to the question.
I’ve changed the LabelStyle to talMark and am handling the chart’s
OnGetAxisLabel. The problem is that my event handler is called for
every X Axis point rather than just the points which displayed labels
when I had LabelStyle set to talAuto.
Right, using the talMark LabelStyle, the axis will draw as many labels as points in the series.
You can use the talAuto LabelStyle to get the number of labels you wish and you can still format the labels in the OnGetAxisLabel event.
UPDATE:
When I use talAuto, ValueIndex is always -1 so I have no idea which
data point is associated with the event.
When talAuto is set and the series has labels, it behaves like talText and talMark: these strings are used in the bottom axis and the ValueIndex in OnGetAxisLabel event can be used.
When talAuto is set but the series has no label, it behaves like talValue: the bottom axis calculates the labels to show in function of the Minimum, Maximum and Increment. The ValueIndex in OnGetAxisLabel event can't be used because the axis labels don't correspond to a series point.
I guess you don't have labels in your series. Otherwise, changing from talAuto to talMark shouldn't change anything.
So you have to decide between one or the other. Maybe you can use talValue (or talAuto without labels) to get the number of labels you wish, and extract the info you need from the string that is going to be drawn.
Alternatively, it could be easier to use CustomLabels. They will allow you to control both the positions and the text of the axis labels without needing any event. In example:
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=false;
Chart1.AddSeries(TBarSeries).FillSampleValues;
Chart1.Axes.Bottom.Items.Clear;
for i:=0 to Chart1[0].Count-1 do
Chart1.Axes.Bottom.Items.Add(Chart1[0].XValue[i], 'label ' + IntToStr(i));
end;
This question relates to another ongoing question here: Format Stones and Pounds correctly?
I figured it would be more suitable to ask here as a separate question, before deciding what to do.
As described from the link above, I am representing weight in a TChart as stones and pounds.
Before I decide on whether to change the data type I am currently using (Double) as the comments from above suggest, I want to see how I can display my chart in Stones and Pounds as I originally had in mind.
See this sample image I manipulated for demo purposes:
The problem is the left Axis of the chart, as the notes in the image state - the chart increments up to .99 (Pounds) before reaching the next whole number (Stones in this case). For example:
03.00
02.99 < .99 = max number before reaching 3.00
02.98
02.97
I need the Left axis to be setup like so:
03.00
02.13 < .13 = max number before reaching 3.00
02.12
02.11
02.10
And also display all the labels for the pounds part.
Is there a setting somewhere that I can use to set the max value for the .00 (lbs) before reaching the next whole number (stones)?
At the moment nothing seems to be making a lot of sense, From what I understand from the other question is I should be using Integers and representing as Pounds only.
I want to see how it could look before making any changes though, ideally I want the chart to stay near the same as the image if possible.
Hope this makes sense.
Thanks.
UPDATE
Just thought I would update the changes I eventually got working.
All credit has to go to David Heffernan for his persistence in explaining where I was going wrong, and writing detailed information and advice.
There is still some work I need to do, but for now here is the updated chart I now have:
I have not done the values in labels, but from the sample image above this is the values I used:
15.2
15.3
15.11
15.13
16.0
16.4
You can't use your current representation to plot a reasonable chart. Suppose you have a bar chart with the following values:
10st 12lb
10st 13lb
11st 0lb
11st 1lb
The chart will look like this:
Forget about the axis labelling for a moment. Look at the difference between the bars. The difference between each adjacent value is 1lb. But the graph tells a completely different story. I've used a bar chart here, but the principle applies for all chart types.
The bottom line is that you need to represent your weight values with a true decimal representation of the weight. Your representation does not obey basic laws of arithmetic. So, half a stone must be 0.5. Trying to represent a half with the number 0.07 is simply asking for a world of pain.
Use this formula to convert from stones and pounds to a true floating point representation of the weight:
FloatingPointWeightValue := Stones + Pounds/14.0;
Or if you store only the pounds then it is simply
FloatingPointWeightValue := Pounds/14.0;
I would suggest that you make sure that the Increment of the axis is at least 1. If you need to go smaller than that then you'll have to get into custom axis label titles. And when you add your values, give them a label. This will allow the chart to use sensible labels to identify values. For example:
Series1.Add(10 + 12/14, '10st, 12lb');
Series1.Add(10 + 13/14, '10st, 13lb');
Series1.Add(11 + 00/14, '11st, 0lb');
Series1.Add(11 + 01/14, '11st, 1lb');
And the result looks like this:
You'd obviously write a bunch of helper functions to handle this. I'd suggest storing the raw data as pounds. So, 11st 1lb would be stored as 11*14 + 1 = 155. Then you'd have these helpers:
function StonesFromPounds(Pounds: Integer): Double;
begin
Result := Pounds / 14.0;
end;
function StonesPoundsLabel(Pounds: Integer): string;
begin
Result := Format('%dst %dlb', [Pounds div 14, Pounds mod 14]);
end;
And then the data population would look like this:
Pounds := 152;
Series1.Add(StonesFromPounds(Pounds), StonesPoundsLabel(Pounds));
You have complete freedom with how you create text labels for your weights. If you want something less verbose than 10st 13lb then you can adjust the helper functions to your needs.
Now, this has been quite a long answer, so I will summarise my advice:
When you acquire the data, from what ever source, convert from whichever format they arrive in to a well-defined storage format, the raw data format.
Store this raw data as integer values, in units of pounds.
When you plot the weights, convert to floating point values in units of stones by Stones := Pounds/14.0.
Associate text labels to each value that you plot so that you can present the weights in human readable form.
Be prepared to supply custom label axes if you need to label weights in between whole values of stones.
I'm evaluating TChartProVCL for XE3.
I created 2 manual series:
1) Series1, a stair-step line
2) Series2, a simple line intersecting Series1 at various points
I then created a calculated series, X Cross Points, which should be the cross points of Series1 and Series2.
It looks fine, except at x=6, and x=5.6 (approx.) This looks like a bug to me.
My ultimate goal, FYI, is to color the regions inside the series. A TRegionBandTool would get close, but I need to color the areas above and below Series1 with different colors.
I'm afraid the TCrossPointsFunction doesn't support sources set with the Stairs mode.
I've added it to the wish list to be implemented in future releases.
In the case of using the "stair-step" series with the TCrossPointsFunction I've discovered a simple "cheat" that is good enough for my purposes. I merely:
Turn OFF the stair-step property.
Create the stair step effect by placing two points very close together on the X axis.
For example:
const
UPPER = 85;
LOWER = 75;
begin
SysThresh.Clear;
for i := 0 to 8 do
SysThresh.AddXY(i, UPPER);
SysThresh.AddXY(8.001, LOWER);
SysThresh.AddXY(12.999, LOWER);
for i := 13 to 25 do
SysThresh.AddXY(i, UPPER);
This gives me the stair step effect without confusing the TCrossPointsFunction.