Page 1 of 1
How to get reasonable X axis markers
Posted: Sun Jun 11, 2017 6:30 am
by 16579037
Hi,
I need to create charts with a logarithmic x axis. Furthermore it is necessary to zoom into the charts.
This leads to strange x axis markers and ticks.
Some examples:
- htklirr_linkst0u42.png (13.63 KiB) Viewed 16398 times
- hmtklirr_linkskiuq5.png (17.88 KiB) Viewed 16403 times
- tmtklirr_rechtsb8upw.png (18.28 KiB) Viewed 16397 times
So how to control the appearance of major, minor ticks and text with different zoom states?
Is there a function for this or is it necessary to draw the x axis by a custom function?
What is a good strategy to get nice and reasonable text markers for a zoomed picture?
Thanks for help
- Uli
Re: How to get reasonable X axis markers
Posted: Mon Jun 12, 2017 8:15 am
by yeray
Hello,
This seems to work as expected for me here:
Code: Select all
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=False;
Chart1.Legend.Visible:=False;
Chart1.Axes.Bottom.Logarithmic:=True;
with Chart1.AddSeries(TLineSeries) as TLineSeries do
begin
Pointer.Visible:=True;
Pointer.Size:=2;
for i:=1 to 11 do
AddXY(exp(i), i);
end;
end;
However, the bottom axis labels overlap if you put more values into that series. This depends on the chart width but I get overlapping axis labels with 23 values.
I've added it to the public tracker:
http://bugs.teechart.net/show_bug.cgi?id=1879
I haven't found extra problems when zooming the chart though.
It would be helpful of you could arrange a simple example we could run as-is to reproduce the problem here.
Thanks in advance.
Re: How to get reasonable X axis markers
Posted: Mon Jun 12, 2017 12:03 pm
by 16579037
Hello Yeroay,
thanks, I'll prep some example code.
In the meantime I have also detected that the appearance of the x axis labes also depends on the window size. So resizing the application window changes the labels.
And this also leads to a problem as even if the desktop window may nicely show up the labels the export of the chart with smaller sizes for documentation will result in pictures with modified labels. And this result is unpredictable from the user point of view.
Beside this there must also be a bug. I will prep some pictures.
Best regards
Uli
Re: How to get reasonable X axis markers
Posted: Wed Jun 14, 2017 9:21 am
by 16579037
Hello Yeray,
I have done some further tests.
The devil is given by Chart.BottomAxis.Increment. By default the value is 0 and we get x axis labels at positions 1, 10, 100, 1000 ...
If the value is changed to 1 then we get x axis labes at 1000, 2000, 3000 ... But if the chart is zoomed the labels can overlap.
Dragging a window corner to resize the window even shows up the x axis labels jumping between different visualizations.
So a solution is to always use Chart.BottomAxis.Increment := 0. As this is a default value where it makes sense to
NEVER TOUCH it!
Now I have still one question (based on a minor tick number of
:
Is it possible to create x axis labes e.g. in sequence 1, 5, 10, 50, 100, 500, 1000, 5000, 10000 ... ??
And finally there is still a bug with zoom:
So if you e.g. create a chart with logarithmic x axis from 1 to 10000 and you zoom into the chart from 150 to 10000 the minor ticks below 1000 get lost.
See the picture, where 8 minor ticks are missed. So there is no chance to estimate the x axis position according to a given curve point:
- AmplitudeTest.png (6.38 KiB) Viewed 16369 times
Best regards
Uli
Re: How to get reasonable X axis markers
Posted: Thu Jun 15, 2017 8:24 am
by yeray
Hello Uli,
UliBru wrote:I have done some further tests.
The devil is given by Chart.BottomAxis.Increment. By default the value is 0 and we get x axis labels at positions 1, 10, 100, 1000 ...
If the value is changed to 1 then we get x axis labes at 1000, 2000, 3000 ... But if the chart is zoomed the labels can overlap.
Dragging a window corner to resize the window even shows up the x axis labels jumping between different visualizations.
So a solution is to always use Chart.BottomAxis.Increment := 0. As this is a default value where it makes sense to
NEVER TOUCH it!
Now I have still one question (based on a minor tick number of
:
Is it possible to create x axis labes e.g. in sequence 1, 5, 10, 50, 100, 500, 1000, 5000, 10000 ... ??
Sounds as the request in
#890 but you you could try using custom labels as follows:
Code: Select all
uses Series, Math;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.Align:=alClient;
Chart1.View3D:=False;
Chart1.Legend.Hide;
with Chart1.AddSeries(TLineSeries) as TLineSeries do
begin
Pointer.Show;
Pointer.Size:=2;
for i:=0 to 10 do
AddXY(exp(i), i);
end;
Chart1.Axes.Bottom.Logarithmic:=True;
Chart1.Axes.Bottom.Increment:=1;
Chart1.Axes.Bottom.Items.Clear;
for i:=0 to 5 do
begin
Chart1.Axes.Bottom.Items.Add(power(10, i), FormatFloat('#,##0.##', power(10, i)));
Chart1.Axes.Bottom.Items.Add(power(10, i)*5, FormatFloat('#,##0.##', power(10, i)*5));
end;
end;
- Project2_2017-06-15_09-53-10.png (17.4 KiB) Viewed 16361 times
UliBru wrote:And finally there is still a bug with zoom:
So if you e.g. create a chart with logarithmic x axis from 1 to 10000 and you zoom into the chart from 150 to 10000 the minor ticks below 1000 get lost.
See the picture, where 8 minor ticks are missed. So there is no chance to estimate the x axis position according to a given curve point:
This sounds as the ticket
#1388. Feel free to add your mail to the Cc list to be automatically notified when an update arrives.
Re: How to get reasonable X axis markers
Posted: Sun Jun 18, 2017 3:50 pm
by 16579037
Yeray,
thanks.
1)
I'll study on using the custom labels. At first look it is ok, but in combination with zooming this may be tricky. Because I may need to design custom labels for each zoom situation.
2)
I have cc'd to the bug list regarding the minor ticks display bug
BR
Uli
Re: How to get reasonable X axis markers
Posted: Tue Jun 20, 2017 2:25 pm
by 16579037
Hello Yeray,
after struggling around with switching linear and logarithmic x-axis including zooming I have now come to the following solution:
Code: Select all
procedure TForm1.Chart1AfterDraw(Sender: TObject);
var
i, j: integer;
maxx, minx, Position: Double;
Chart: TChart;
begin
Chart := TChart(Sender);
maxx := Chart.BottomAxis.Maximum;
minx := Chart.BottomAxis.Minimum;
if maxx = minx then
exit;
if Chart.BottomAxis.Logarithmic then
begin
for i := -1 to 5 do
for j := 2 to 9 do
begin
Position := Power(10, i) * j;
if (Position >= minx) and (Position <= maxx) then
DrawMinorTick(Chart, Power(10, i) * j);
end;
end;
end;
procedure TForm1.Chart1BeforeDrawAxes(Sender: TObject);
var
i: integer;
maxx, minx: Double;
Chart: TChart;
begin
Chart := TChart(Sender);
maxx := Chart.BottomAxis.Maximum;
minx := Chart.BottomAxis.Minimum;
if maxx = minx then
exit;
Chart.Axes.Bottom.Items.Clear;
if Chart.BottomAxis.Logarithmic then
begin
Chart.BottomAxis.MinorTickCount := 0;
if (minx > 0.1) and (maxx < 1) or (minx > 1) and (maxx < 10) or (minx > 10) and (maxx < 100) or (minx > 100) and
(maxx < 1000) or (minx > 1000) and (maxx < 10000) or (minx > 10000) and (maxx < 100000) or (minx > 100000) and
(maxx < 1000000) then
begin
Chart.Axes.Bottom.Items.Clear;
Chart.Axes.Bottom.Items.Add(minx, Format('%1.0f', [minx]));
Chart.Axes.Bottom.Items.Add(maxx, Format('%1.0f', [maxx]));
end
else
begin
for i := -1 to 5 do
begin
Chart.Axes.Bottom.Items.Add(Power(10, i), Format('%1.0f', [Power(10, i)]));
// Chart.Axes.Bottom.Items.Add(Power(10, i) * 5, Format('%1.0f', [Power(10, i) * 5]));
end;
if (minx <> 0.1) and (minx <> 1) and (minx <> 10) and (minx <> 100) and (minx <> 1000) and (minx <> 10000) and
(minx <> 100000) then
Chart.Axes.Bottom.Items.Add(minx, Format('%1.0f', [minx]));
if (maxx <> 0.1) and (maxx <> 1) and (maxx <> 10) and (maxx <> 100) and (maxx <> 1000) and (maxx <> 10000) and
(maxx <> 100000) then
Chart.Axes.Bottom.Items.Add(maxx, Format('%1.0f', [maxx]));
end;
end
else // linear axis
begin
Chart.Axes.Bottom.Items.Automatic := true;
Chart.BottomAxis.MinorTickCount := 3;
end;
end;
So the solution shows the labels in Power10 steps. If you zoom in and a power10 label is still there it will be shown. In addition the chart now also displays the lowest and highest x value. So if you zoom more into the chart at least min and max x value are displayed as label.
Furthermore the minor ticks are custom drawn and thus are correct for each zoom state. The reported bug is at least also solved by this approach.
Also the export e.g. to PNG now properly works.
So maybe you can benefit a little from my example.
Best regards
Uli
Re: How to get reasonable X axis markers
Posted: Wed Jun 21, 2017 8:11 am
by yeray
Hello Uli,
Thanks for the code! I've added a link to it in the
#1388 ticket.
Re: How to get reasonable X axis markers
Posted: Wed Jun 21, 2017 2:56 pm
by 16579037
Hello Yeray,
I have refined the code for correctness, speed and clarity.
It contains some specialties as a user can zoom into between the major ticks. The minor ticks are still shown, the chart ends show labels to inform about the x axis values.
If there is a major label too close to a chart end the according end label is not displayed.
Code: Select all
procedure TForm1.Chart1AfterDraw(Sender: TObject);
var
i: integer;
maxx, minx, tickpos, tickposx: Double;
Chart: TChart;
procedure DrawMinorTick(value: Double);
var
xpos, ypos: integer;
begin
Chart.Canvas.Pen.Color := Chart.BottomAxis.MinorTicks.Color;
Chart.Canvas.Pen.Width := Chart.BottomAxis.MinorTicks.Width;
Chart.Canvas.Pen.Style := Chart.BottomAxis.MinorTicks.Style;
xpos := Chart.BottomAxis.CalcPosValue(value);
ypos := Chart.BottomAxis.PosAxis + 1;
Chart.Canvas.Line(xpos, ypos, xpos, ypos + Chart.BottomAxis.MinorTickLength);
end;
begin
Chart := TChart(Sender);
maxx := Chart.BottomAxis.Maximum;
minx := Chart.BottomAxis.Minimum;
if maxx = minx then // chart may be empty
exit;
if Chart.BottomAxis.Logarithmic then
begin
tickpos := 0.01;
repeat
for i := 2 to 9 do // = 8 minor ticks
begin
tickposx := tickpos * i;
if tickposx < minx then
continue;
if tickposx > maxx then
break;
DrawMinorTick(tickposx);
end;
tickpos := tickpos * 10;
until tickposx > maxx;
end;
end;
procedure TForm1.Chart1BeforeDrawAxes(Sender: TObject);
var
maxx, minx, xpos, minlabelpos, maxlabelpos: Double;
inbetween: Boolean;
Chart: TChart;
procedure AddXLabel(xpos: Double);
begin
if (xpos >= 1) then
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.0f', [xpos]))
else if (xpos >= 0.1) then
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.1f', [xpos]))
else
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.2f', [xpos]));
end;
procedure AddMinMaxXLabel(xpos: Double);
begin
if (xpos >= 10) then
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.0f', [xpos]))
else if (xpos >= 1) then
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.1f', [xpos]))
else
Chart.Axes.Bottom.Items.Add(xpos, Format('%1.2f', [xpos]));
end;
begin
Chart := TChart(Sender);
maxx := Chart.BottomAxis.Maximum;
minx := Chart.BottomAxis.Minimum;
if maxx = minx then // chart may be empty
exit;
Chart.Axes.Bottom.Items.Clear;
if Chart.BottomAxis.Logarithmic then
begin
Chart.BottomAxis.MinorTickCount := 0; // clear automatic minor ticks, custom draw in OnBeforeAxes
// check for absence of major ticks
xpos := 0.01;
inbetween := false;
repeat
if ((minx > xpos) and (maxx < xpos * 10)) then
inbetween := true;
xpos := xpos * 10;
until xpos > maxx;
if inbetween then // no major ticks
begin
AddMinMaxXLabel(minx);
AddMinMaxXLabel(maxx);
end
else
begin
// draw major ticks + labels
minlabelpos := 1000000;
maxlabelpos := 0;
xpos := 0.01;
repeat
if (xpos >= minx) then
begin
if xpos > maxx then
break;
AddXLabel(xpos);
// AddXLabel(Position*5);
if xpos < minlabelpos then // memorize minlabel
minlabelpos := xpos;
if xpos > maxlabelpos then // memorize maxlabel
maxlabelpos := xpos;
end;
xpos := xpos * 10;
until xpos > maxx;
// possibly draw labels at chart ends
if minx <= 0.9 * minlabelpos then
AddMinMaxXLabel(minx);
if maxx >= 1.2 * maxlabelpos then
AddMinMaxXLabel(maxx);
end;
end
else // linear axis
begin
Chart.Axes.Bottom.Items.Automatic := true;
Chart.BottomAxis.MinorTickCount := 3;
end;
end;
Have fun
Uli
Re: How to get reasonable X axis markers
Posted: Thu Jun 22, 2017 6:24 am
by yeray
Thanks for sharing, Uli!