|
Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-22-2021 23:37 |
|
Hello guys,
im considering buying ChartDirector, but first I'd love to figure two things out:
1. I have a XYChart which i'd like to 'divide' in two areas, the first area contains a line chart and the second one has horizontal bars where i want to place text in. Is there a way to place Textvalue into these bars with x and y coordinates (like 15:00 and -25)?
2. Is it possible to remove all labels on the yAxis below 0?
thanks in advance!
|
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Aug-23-2021 15:55 |
|
Hi Medic89,
1. There are many ways to add custom text in ChartDirector. If the coordinates are axis coordinates (as opposed to pixel coordinates), one method is to add a scatter layer with invisible symbols at the positions you want to have the labels. You can then add labels to those invisible symbol positions.
The exact code depends on your programming language. In C#, it is like:
(If you need to translate the following code to another language, please let me know.)
// x and y data coordinates of the symbols, and their labels
double[] xValue = { ..... };
double[] yValue = { ..... };
string[] labels = { ..... };
// Add transparent symbols.
ScatterLayer labelLayer = c.addScatterLayer(xValue, yValue, "", Chart.SquareSymbol, 1, Chart.Transparent, Chart.Transparent);
// Add the labels as a custom labels center aligned to the symbols
for (int i = 0; i < labels.Length; ++i) {
ChartDirector.TextBox t = labelLayer.addCustomDataLabel(0, i, labels[i], "Arial Bold", 8, 0x000000);
t.setAlignment(Chart.Center);
}
Note that for the x-data coordinates, if your x-axis is configured as labels (using Axis.setLabels), then the x-data coordinate it the array index of the labels. For example, the "15:00" in your chart would be x = 12 as it is the 13th label. You can use a non-integer (like 11.5) if you want to put the custom label in between two ticks.
2. Is it possible to remove all labels on the yAxis below 0?
If the yAxis scale is configured with your own code, you can simply not adding the labels below zero. You can use Axis.setLinearScale2 to control the labels, and use empty space for labels below zero.
If the yAxis scale is automatically determined by ChartDirector, the labels depend on your data and are therefore unpredictable. You can then use the following method:
.... create the chart as usual ....
// Before displaying the chart, layout the axis first. ChartDirector will determine
// the axis scale and labels from your data.
c.layoutAxes();
// Can obtain the label positions now
double[] ticks = c.yAxis().getTicks();
// Replace all labels below 0 with empty space
for (int i = 0; i < ticks.Length; ++i) {
if (ticks[i] < 0)
c.yAxis().addLabel(ticks[i], "");
}
... display the chart as usual ...
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-23-2021 21:55 |
|
Hello Peter,
thank you very much for your instant reply (Im using C++ MFC btw).
1. It works, the only problem is, there is a Margin, even if I place the label at 15:00 and Chart::Left, the text doesn't start exactly at 15:00; second, I tried to place text at start of the horizontal bars, is there a way to place the labels, so they end shortly before the start of the bar?
2. In my first build I set the yAxis to setLinearScale, because I want a step of 10 (regardless of the range of the yAxis), so I can't autoscale the yAxis. I tried to use the setLinearScale2 method, but this isn't available (VC says, that class axis doesn't have a member called setLinearScale2).
Would you suggest a better way to display the bars underneath the main chart? Currently Im using "addZone".
Do you know, if its possible to set the first Zone beneath the xAxis to non-transparent (actually I dont want to see the mainGridlines in this white Area). |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Aug-24-2021 02:24 |
|
Hi Medic89,
1. The TextBox does have a default margin. You can use TextBox.setMargin to set the margin to 0.
2. For C++, the setLinearScale2 is implemented as "setLinearScale" instead. It is because C++ supports overloading, which means we can have two methods of the same name but different argument types: "void setLinearScale(double, double, DoubleArray)" and "void setLinearScale(double, double, StringArray)". In some other languages, it is better to use different method names.
However, before you do any of the above, is the intention of the horizontal bars are just for providing some colored cells for labelling? If this is the case, may be it is easier to put data tables under the x-axis. The followings are two examples included in ChartDirector:
https://www.advsofteng.com/doc/cdcpp.htm#datatable.htm
https://www.advsofteng.com/doc/cdcpp.htm#datatable2.htm
With the data table method, you do not need to extend the y-axis to negative values. It stops at y = 0. So there will be no labels and no grid lines for y < 0.
In the examples above, the table replaces the x-axis labels. For your case, if you want to keep the original x-axis labels, you can add another x-axis below the original x-axis. Our sample code includes an example for multiple y-axes. It also works for x-axes too.
https://www.advsofteng.com/doc/cdcpp.htm#multiaxes.htm
For your case, one method is:
// Add an axes 20 pixels below the bottom x-axis
Axis* labelAxis = c->addAxis(Chart::Bottom, 20);
// Add the first row of labels. You can use an empty string "" for cells with no labels
labelAxis->setLabels(StringArray(firstRowOfLabels, labelCount));
// Turn the axes labels into a table
CDMLTable* t = labelAxis->makeLabelTable();
// Configure the table background color and text style and alignment
TextBox *tStyle = t->getRowStyle(0);
tStyle->setFontStyle("Arial Bold");
tStyle->setBackground(0xcccccc, 0xffffff);
tStyle->setMargin(0);
tStyle->setAlignment(Chart::Left);
// Add another row of labels
t->appendRow();
for (int i = 0; i < labelCount; ++i)
{
tStyle = t->setText(i, 1, secondRowOfLabels[i]);
tStyle->setFontStyle("Arial Bold");
tStyle->setBackground(0x88ccff, 0xffffff);
tStyle->setMargin(0);
tStyle->setAlignment(Chart::Left);
}
Hope this can help.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-25-2021 01:34 |
|
Hello Peter,
again, thanks for your hard work, my chart is working pretty good now, however I experienced two more problems:
1. I wanted to get the information about the labels on the yAxis from a multi dimensional array, how do I set that up? I have an array with x columns and 2 rows, how do I get c++ to use the values from the first column of my array to display on the yAxis?
z->yAxis()->setLabels(StringArray(Array[?], Array_size));
2. I wanted the labels on the yaxis to be displayed in the middle of the column, so I added an offset 0.5 times of the labelstep, however, the last value of the label array doesn't get displayed anymore. |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Aug-25-2021 16:04 |
|
Hi Medic98.
1. By "multi-dimensional array". I assume it means an array of arrays, that is, a text string can be accessed as myArray[x][y]. I assume by "text string", you mean "char *" (as opposed to std::string or MFC CString or some other implementation).
If the first index is the row and the second index the column, then the row is an array of text strings, and you can simply use "StringArray(myArray[0], columnCount)".
If the first index is the column and the second index the row, then the row is not an array of text strings. You would need to copy the row strings to an actual array. It is like:
std::vector<const char *> myRow;
for (int i = 0; i < column_count; ++i)
myRow.push_back(myArray[i][0]);
c->xAxis()->setLabels(StringArray(&(myRow[0]), column_count));
2. By default, the labels are associated with axis ticks. If there are 10 labels, there will be 10 ticks. You can shift the labels so that they are in between the ticks, but for 10 ticks, there can only be 9 positions in between the ticks, so only 9 labels can be displayed. So it is normal the last label is not displayed. If you really want to display 10 labels, you would need to have a label array with 11 labels (11 ticks). The last label can be an empty space.
A related issue is where do you want to position the data points. By default, the data points will be positioned at the grid lines, which aligns with the ticks. If you only have 10 data points, but 11 ticks, then the last grid line will not have any data point.
In some charts that represents the data as large objects (such as as bars), it is common to put the data object in between the ticks too. The following are two examples:
https://www.advsofteng.com/doc/cdcpp.htm#softlightbar.htm
https://www.advsofteng.com/doc/cdcpp.htm#datatable.htm
Note in the above, the data objects (bars and points) and the labels are in between the ticks. It is achieved by shifting the ticks, not the labels. For a line chart, it can be done with:
// Add 0.5 units to either end of the axis. This is the default for charts with large data
// objects, like a bar chart or box whisker chart, or for an axis with a label table.
// For other charts, we need to call it explicitly.
c->xAxis()->setIndent(true);
// Shift the ticks
c.xAxis().setTickOffset(0.5);
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-25-2021 21:40 |
|
Hello Peter,
yes, the First Index of my Array is the row, and the second the column, I initialize it as follos:
const char* Labels[][2]: { {"Label A", ""},
{"Label B", ""},
{"Label C", ""},
{"Label D", ""},
{"Label E", ""} };
const int Labels_size = (int)(sizeof(Labels) / sizeof(*Labels));
Now I want the first column to be the labels:
z->yAxis()->setLabels(StringArray(Labels[0], Labels_size));
But on the chart, every Value (also those from column 2) get displayed on the axis. |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Aug-25-2021 22:04 |
|
Hi Medic89.
May be there is some confusion of what is the row and what is the column. As you mentioned there were two rows, I assumed the "Label A", "Label B", .... "Label E" were the first row, and the 5 empty strings were the second row. What I was thinking is something like:
const char* Labels[2][5] =
{ {"Label A", "Label B", "Label C", "Label D", "Label E"},
{"", "", "", "", ""}};
In the above, the first index is [2] is the row index, and the second index [5] is the column index. With the above, you can use "StringArray(Labels[0], 5)".
If the array is declared like the one in your last message, you can use:
std::vector<const char *> myRow;
for (int i = 0; i < 5; ++i)
myRow.push_back(Labels[i][0]);
z->yAxis()->setLabels(StringArray(&(myRow[0]), Labels_size));
Hope this can help.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-25-2021 23:53 |
|
Hello Peter,
actually its the oposite, "Label A", "Label B" and so on are the values of the first column, the empty values belong to the second column.
Another question, A little above you taught me how to add texboxes to the chart, now I'd like to place a line behind the textbox, do you know, how to get the position of the textbox on the Basechart? |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Aug-26-2021 22:58 |
|
Hi Medic89.
For the labels, the method that uses std::vector should work for your case.
For adding "textboxes" to the chart, are you referring to the TextBox added using "Layer::addCustomDataLabel"? (The TextBox is a object class in ChartDirector. Most of the text in ChartDirector - such as chart title, axis labels, table cells, data labels, etc, are TextBox objects.)
In many cases, your code asks ChartDirector to put the TextBox at a particular position. That means you may already know the position before adding the TextBox, because you need to tell ChartDirector where to add the TextBox in the first place.
For example, in one of the post, I describe using a scatter layer with transparent symbols, and add a TextBox for the symbols. The positions are the positions of the transparent symbols. These positions are provided by your code, so your code already knows the positions.
If you need further help, is it possible to be more specific on which TextBox you are referring to?
For adding a line, if the line is defined using axis coordinates (the coordinates you use to plot the line chart and is displayed on the x and y axes), then you can just add a line layer for the line. The following is an example of adding arbitrary lines with axes coordinates:
https://www.advsofteng.com/doc/cdcpp.htm#xyline.htm
(Note that for label based axis, that is, axis configured using Axis.setLabels, the coordinate is the array index of the label, and not the text of the label.)
If you want to add a line using pixel coordinates, you can use BaseChart.addLine. Pixel coordinates are often used for things that are not specific to the data. For example, if you want to add a copyright message at the bottom-left corner of the plot area, you can add the text using pixel coordinates.
If you need further help, please also let me know the nature of the line you want to add.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-27-2021 22:42 |
|
Hello Peter,
I'd like to draw a line in the middle of the "bar" with the start point at the end of the textbox (I used to draw a frame around the textbox and I want the line to be "added" at the end of the textbox.
Another question:
I have an yAxis with linearscale and I added custom textlabels to each major tick, which I set an offset of 0.5 to, so they are in the middle of each "row". When I try to add some datapoints to the chart, I set the actual value, the x coordinate is the time mark on the xAxis and the y Coordinate is the position of the yAxis as double (in my program, the first row goes from 0 to 1, so the y coordinate would be 0.5), is it possible to replace the 0.5 with thetext of the label at the 0.5 position? |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Aug-31-2021 22:55 |
|
Regarding the lines:
in my chart, the textboxes who go into the chart are in no particular order, but I want the lines to be drawn for each line separately, so id be easier, if I could say draw a line from the end of textbox X until 12:00 O'Clock. |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-02-2021 01:19 |
|
Hi Medic89.
I am not too sure what type of line you want to draw. I do not even know if it is a vertical line or horizontal line. From your description that "you used to draw a frame around the textbox", my guess is that you want to draw a short vertical line instead of the frame, for the purpose of separate one text box with the next text box.
If you are referring to the text boxes under the x-axis, the end of a text box is the next tick position. You can use BaseChart.addLine to add a vertical line at that position.
If the above is not what you need, is it possible to provide a sample mock up image (eg. creating using Windows Paint) to help me understand your requirement?
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-02-2021 01:28 |
|
Hi Medic89,
For your other question "is it possible to replace the 0.5 with the text of the label at the 0.5 position?", I assume you would like to replace the 0.5 you use to specify the position of the data point. Unluckily, the x and y coordinates must be numbers in ChartDirector.
In ChartDirector, labels are just names. They do not need to be unique (so it is OK to use the same labels multiple times on an axis, which is often used for some chart), and they are just for display and have no other meaning. They cannot be used to specify positions. The XYChart layers, API only accept DoubleArray to specify positions.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-02-2021 19:49 |
|
Hello Peter,
regarding the lines, here is an example what I'd like to have in CHartdirector. The Box with the number in it is the textbox I create in the code, and then I'd like to place a line from directly behind the box to a certain time value (in the picture it is 02:15). |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-02-2021 19:49 |
|
Forgot the picture...
|
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-03-2021 01:05 |
|
Hi Medic89.
I understand your requirement now.
If I am to do it myself, I would add the text box using BaseChart.addText and the line using BaseChart.addLine. The code structure is then like:
... create chart as usual ...
// layout the axes so we can convert from axis coordinates to pixel coordinates
// (The BaseChart.addTextBox and BaseChart.addLine expects pixel coordinates.)
c->layoutAxes();
int startX = c->getXCoor(arrayIndexOfStartingPoint);
int endX = c->getXCoor(arrayInexOfEndingPoint);
TextBox *t = c->addText(startX, textY, "2", .....);
... configure text box border ...
c->addLine(t->getLeftX() + t->getWidth(), textY, endX, textY, 0x000000, 5);
If you have some complicated bars, another method you may consider is to put a gantt chart under your chart. A gantt chart is like:
https://www.advsofteng.com/doc/cdcpp.htm#colorgantt.htm
You can use Axis.syncAxis to make sure the gantt chart is sychronized with your current chart. The gantt chart can be labelled in various ways including like that in your image.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-03-2021 01:06 |
|
Hi Medic89.
I forgot to mention that you can use a MultiChart to combine your current and the gantt chart into one chart.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-03-2021 23:48 |
|
Wow Peter, that works terrific;-)
One thing that came up to me is, is it possible to display UTF-16 Unicode symbols as labels? I was able to include UTF-8 unicode symbols to variables and display them on the chart:
const char* Ende = u8"u25CB"; |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-04-2021 01:51 |
|
Hi Medic89,
In C++, UTF-16 symbols are of type "wchar_t *". For UTF-16 string literals, the C++ syntax is L"abcd".
ChartDirector only accept "const char *" for strings. So if you use UTF-16, it needs to be converted to UTF-8 before passing it to ChartDirector. To make it easy, ChartDirector includes an inline conversion utility. See:
https://www.advsofteng.com/doc/cdcpp.htm#WCHARtoUTF8.htm
In MFC, it is common to express a text string as a TCHAR. (TCHAR is the type used by MFC objects such as CString. It can be equivalent to either char or wchar_t, depending on the MFC project configuration.) ChartDirector also includes a utility to convert TCHAR * to UTF-8 strings:
https://www.advsofteng.com/doc/cdcpp.htm#TCHARtoUTF8.htm
Hope this can help.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-04-2021 18:52 |
|
Hello Peter,
ok, I tried this:
WCHARtoUTF8 Defibrillation = L"u21AF";
but the utf16 sign (a zick-zack arrow) doesn't show up on the chart.
Another problem I currently have:
I going to place labels on my chart with this function:
TextBox* WerteGruppe4 = Medikamente4->addCustomDataLabel(0, i, &(Schilder4[i][0]), "Arial", 12, 0x000000);
WerteGruppe4->setAlignment(Chart::Left);
The Array 'Schilder' has 7 items and i is part of a loop that runs from 0 to 6.
when I look at the array, it gives me the following values:
Schilder4[7][0] = {"Medikamente","Verworfen","Total","25","250","25 mg","250 mg"};
But when the placing of the labels is complete, the last two labels are both "250 mg", do you know where the mistake is? |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-04-2021 19:39 |
|
OK, now I got the problem, I have another loop, where I calculate certain values to an integer variable ("m"), but in order to write that into array, I have to convert them to char. It looks like, that in each round, all the previously calculated values inside the array "Schilder" get overwritten with the current value...
|
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-05-2021 00:05 |
|
Hi Medic89,
There is only one buf array in your code, so everytime you write to the buf array, it overheads the previous content.
The Schilder does not contain the content of the buf array. It only contains a pointer to the buf array, and all the pointers point to the same buf array, because there is only one buf array in your code.
Your code should push a copy of the text string in the buf array to the Schilder. In this case, it does not matter if the buf array is overwritten later, because the Schilder contains a copy of the text string.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-05-2021 00:32 |
|
Sorry for the stupid question Peter, what exactly do you mean by "push a copy of the string into Schilder"? You mean I should get the string into another variable? |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-05-2021 01:22 |
|
Here is what I've tried so far, but without success:
|
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-05-2021 04:11 |
|
Hi Medic89,
Starting from your original code, instead of:
Schilder5.push_back(buf);
You may use:
Schilder5.push_back(strdup(buf));
The strdup is a standard C function to make a copy of a text string:
https://en.cppreference.com/w/c/experimental/dynamic/strdup
As mentioned in the above page, strdup will allocate memory to make the copy. After drawing the chart, please remember to "free" the text strings to avoid memory leak.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-05-2021 17:39 |
|
Thank you Peter, now i got it to run, altough I had to use "_strdup"
Do you have a tip regarding the UTF16 unicode symbol? I tried it as shown on my last screenshot, but I only get a square on my chart.
|
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-06-2021 10:36 |
|
Hi Medic89,
The square character means your font does not have the unicode character. For example, if you use the Arial font, the font cannot support Korean characters. Please make sure the font you use contains the character.
Regards
Peter Kwan |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Medic89 on Sep-06-2021 22:22 |
|
Hi Peter,
I just copied the sign to Excel and I can convert it to Arial, it stil gets displayed on the screen. (Thats the sign Im looking for: "↯" U+21AF) |
Re: Remove negative yAxis labels and place textboxes with x/y coordinates |
Posted by Peter Kwan on Sep-06-2021 23:34 |
|
Hi Medic89,
In Excel, if the font does not support the character, Excel will search for another font that does contain the character, so the actual font used may not be Arial. The exact font used depends on what fonts are available on the computer.
ChartDirector is more like MS Word or PDF, which is more concern with accurate layout. In MS Word, if the Arial font is used, and it will display your character as a square, the same as what ChartDirector does.
You can try this:
- Copy the character from Excel
- Paste it to MS Word
In my computer, the MS Word can display the character but its font becomes "Cambria Math". This shows that in my computer, Excel finds the "Cambria Math" that contains the character. In another computer, it may choose another font. If you change the font back to Arial in MS Word, the character will become a square.
On Windows, there is an application called "Character Map". It will display all characters that exists in a font. You can try to look at the Arial font. It does not contain U+21AF. The nearest character Arial contains is U+21A8, then the next character jumps to U+2022.
For your case, to solve the problem, instead of using Arial, please choose a font that contains the character you need.
Regards
Peter Kwan |
|