Create AChartEngine TimeChart using Time in Y-Axis - java

I have some data representing answered questions in particular time e.g.
Question 1 answered in 00:00:20.
I am trying to use AChartEngine to represent this but with no luck.
First of all I can't have Y values as this format for a reason, guess it's not supported or needs customization which couldn't find a way till now to achieve.
My chart should have in the end the X-Axis with values 1, 2, 3, 4, 5.... and Y-Axis with
00:00:20, 00:00:15, 00:00:05, 00:00:10....
The time achieved in each question is saved in a Time object field.
I try this approach:
private void fillData() {
int i = 0;
for (Answer answer : getAnswers()) {
i++;
if (answer.getEstimatedAnswerTime() != null) {
Log.d(TAG, String.valueOf(answer.getEstimatedAnswerTime()));
myQuestionsTimeSeries.add(new Date(DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime())), i);
}
}
}
First problem, can't get the time values to Y-Axis and not shown correctly anyway. See screenshot below.
I guess you get my point so far.
Thank you in advance.

The TimeChart displays formatted date labels on the X axis. It seems like you need to do that on the Y axis.
Just create a regular LineChart and add custom labels on the Y axis:
// disable the default Y labels first
renderer.setYLabels(0);
// add several custom labels
renderer.addYTextLabel(y, "label");

Related

iText cell widths seem to behave inconsistently

I have a function which I've been using for a good while in java which calculates cell widths for a pdftable from a given array of string values.
It works very well, and I recently wrote a version of the function in c# and it doesn't give the expected result (i.e text is wrapped to multiple lines) - both java and c# code shown below any help much appreciated
This is the Java version;
float[] CalculateCellWidths(String[] CellHeaders, Font CellFont)
{
float[] CellWidths = new float[CellHeaders.length];
for (int i = 0; i < CellHeaders.length; i++)
{
CellWidths[i] = CellFont.getCalculatedBaseFont(true).getWidthPoint(CellHeaders[i], CellFont.getCalculatedSize());
}
return CellWidths;
}
This is the C# version;
float[] CalculateCellWidths(String[] CellHeaders, iTextSharp.text.Font CellFont)
{
float[] CellWidths = new float[CellHeaders.Length];
for (int i = 0; i < CellHeaders.Length; i++)
{
CellWidths[i] = CellFont.GetCalculatedBaseFont(true).GetWidthPoint(CellHeaders[i], CellFont.CalculatedSize);
}
return CellWidths;
}
I see from your comment that the problem has been solved, but that you don't know why, so here's a small explanation about LockWidth.
There are two ways to define the width of a table. Either the table takes a percentage of the available width. This is a relative width that depends on page size and page margins. Historically, this percentage is 80% and the table is centered. You can change this width percentage with the setWidthPercentage() method. If you use a method to set the widths of the invidual columns, those widths will be treated as relative widths, for instance { 10, 10, 20 } means that you have a table with 3 columns where the first two columns take a quarter of the available width and the third column takes half.
You can also set the absolute width of a table. You can set the total width with the setTotalWidth() method. You can also define the absolute widths of the columns. However, these absolute widths are ignored in favor of the default width percentage as long as you don't "lock" the width. This is what happens if you use setLockedWidth(true) (Java) or table.LockedWidth = true (C#).
Based on your comment, I think the problem was caused by locking the width of the columns so that absolute values were used instead of relative values.

JavaFx Charts and getting values for Y Values from XYChart.Series knowing the X values

I'm attempting to replicate something I've hand crafted in the past but I'm keen to move this to JavaFX Charts so I know longer need to maintain it myself. I can get the XAxis and YAxis values from a chart relatively easily based on the location of the mouse pointer. But I now need to know what the series value (and I might have many) is. I then plan to display this in a box that floats over the charts as shown in my hand crafted chart.
I'd post an example image but sadly my reputation is currently too low.
I know I can do this by manually recording very value add to the chart in a Tree Map and then polling it as the mouse moves to determine the values... But I'm curious if there's a simpler approach that doesn't require me to repeat code from my existing non JavaFX chart.
I can't find anything in XYChart.Series or Observable List that would simplify this process so I'm just trying a sanity check before I hand crank it myself.
You can do something like this:
But your X must be a Number value
private Data<X, Y> getNearestDataForXValue(X xValue, Series<X, Y> series) {
Data<X, Y> nearsetData = null;
double distance = Double.MAX_VALUE;
for (Data<X, Y> data : series.getData()) {
double xData = data.getXValue().doubleValue();
double dataDistance = Math.abs(xValue.doubleValue() - xData);
if (dataDistance < distance) {
distance = dataDistance;
nearsetData = data;
}
}
return nearsetData;
}

AChartEngine Y-Axis custom labels area margins and chart values as String?

I have created a Chart shows Questions (X) / Time (Y) answered during a test.
You can see the first question here for details.
But now I need to show the chart bullet values correctly, at the moment shows the milliseconds value but i need to show the custom hh:mm:ss value like I've done with the Y-Axis label and somehow customize the Y-Axis area to show the full values correctly.
Below is a screenshot of how the chart looks like now.
[EDIT]
With Dan's help I almost got what I want. It's just a little problem.
Check in the screenshot below where the chart values now appearing.
I updated to 1.1.0 from the AChartEngine repository.
addNotations is on the TimeSeries objects. I copy paste my code below where adding data to my TimeSeries instance.
myQuestionsTimeSeries.add(i, DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()));
xyMultipleSeriesRenderer.addYTextLabel(DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()),
String.valueOf(answer.getEstimatedAnswerTime()));
myQuestionsTimeSeries.addAnnotation(String.valueOf(answer.getEstimatedAnswerTime()), i,
DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()));
The code results to this Chart:
[EDIT]
This is basically the whole class:
private void initQuestionsTimeChart() {
xyMultipleSeriesDataset = new XYMultipleSeriesDataset();
xyMultipleSeriesRenderer = new XYMultipleSeriesRenderer();
questionsTimeChart = ChartFactory.getLineChartView(getActivity(), xyMultipleSeriesDataset, xyMultipleSeriesRenderer);
rootView.addView(questionsTimeChart);
initSeriesData();
}
private void initSeriesData() {
createMyQuestionsSeries();
addSeriesAndRenderer(myQuestionsTimeSeries, myQuestionsRenderer);
xyMultipleSeriesRenderer.setYTitle("Questions Time");
xyMultipleSeriesRenderer.setXTitle("Questions Number");
xyMultipleSeriesRenderer.setMarginsColor(Color.argb(0, 255, 255, 255));
xyMultipleSeriesRenderer.setAxesColor(Color.BLACK);
xyMultipleSeriesRenderer.setLabelsColor(Color.BLACK);
xyMultipleSeriesRenderer.setXLabelsColor(Color.BLACK);
xyMultipleSeriesRenderer.setYLabelsColor(0, Color.BLACK);
xyMultipleSeriesRenderer.setAxisTitleTextSize(16);
xyMultipleSeriesRenderer.setLabelsTextSize(15);
xyMultipleSeriesRenderer.setYLabelsAlign(Paint.Align.RIGHT);
xyMultipleSeriesRenderer.setSelectableBuffer(20);
xyMultipleSeriesRenderer.setYLabels(0);
xyMultipleSeriesRenderer.setMargins(new int[]{ 80, 80, 80, 80 });
}
private void addSeriesAndRenderer(XYSeries series, XYSeriesRenderer renderer) {
xyMultipleSeriesDataset.addSeries(series);
xyMultipleSeriesRenderer.addSeriesRenderer(renderer);
}
private void createMyQuestionsSeries() {
myQuestionsTimeSeries = new TimeSeries("My Questions/Time");
myQuestionsRenderer = new XYSeriesRenderer();
myQuestionsRenderer.setColor(Color.BLUE);
myQuestionsRenderer.setLineWidth(3f);
myQuestionsRenderer.setPointStyle(PointStyle.CIRCLE);
myQuestionsRenderer.setFillPoints(true);
myQuestionsRenderer.setChartValuesSpacing(10f);
}
private void fillData() {
int i = 0;
for (Answer answer : getAnswers()) {
i++;
if (answer.getEstimatedAnswerTime() != null) {
myQuestionsTimeSeries.add(i, DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()));
xyMultipleSeriesRenderer.addYTextLabel(DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()),
String.valueOf(answer.getEstimatedAnswerTime()));
myQuestionsTimeSeries.addAnnotation(String.valueOf(answer.getEstimatedAnswerTime()), i,
DateTimeHelper.getMillisFromTime(answer.getEstimatedAnswerTime()));
}
}
}
Thank you in advance!
First of all, hide the chart values:
renderer.setDisplayChartValues(false);
Then, for each chart value, add an annotation:
renderer.addAnnotation("text", x, y);
For the Y axis labels to be visible, just align them to LEFT:
renderer.setYLabelsAlign(Align.LEFT);
Or you can increase the margins:
renderer.setMargins(margins);
Make sure you are using the latest ACE build available here.

Is it possible to change the x- and y-axis caption dynamically with the vaadin addon InvientCharts

I am using vaadin and for some visual data analysis I've added the addon InvientCharts for vaadin (https://vaadin.com/directory#addon/invient-charts).
Is it possible to dynamically change the x- and y-axis Caption of the scattertchart (so after the chart has been created)?
I'm currently having a scatterchart and a button. When the button is clicked, all existing points (Series) shall be removed, the x- and y-axis caption shall change and the new points shall be added on the chart.
That's the code snippet with which I'm trying it currently:
public void changePoints(String xAxisTitle, String yAxisTitle, List<List<double[]>> xAndYCoordinates) {
// remove all points from the scatterchart - THIS IS WORKING
Object[] allSeries = chart.getAllSeries().toArray();
for(int j = 0; j < allSeries.length; j++){
Series serie = (Series) allSeries[j];
chart.removeSeries(serie);
}
// update the x- and y-axis - THIS IS NOT WORKING AND WHAT I'M TALKING ABOUT
chartConfig.getXAxes().clear();
chartConfig.getYAxes().clear();
NumberXAxis xAxis = new NumberXAxis();
xAxis.setTitle(new AxisTitle(xAxisTitle));
xAxis.setStartOnTick(true);
xAxis.setEndOnTick(true);
xAxis.setShowLastLabel(true);
LinkedHashSet<XAxis> xAxesSet = new LinkedHashSet<InvientChartsConfig.XAxis>();
xAxesSet.add(xAxis);
chartConfig.setXAxes(xAxesSet);
NumberYAxis yAxis = new NumberYAxis();
yAxis.setTitle(new AxisTitle(yAxisTitle));
LinkedHashSet<YAxis> yAxesSet = new LinkedHashSet<InvientChartsConfig.YAxis>();
yAxesSet.add(yAxis);
chartConfig.setYAxes(yAxesSet);
// add the new points - THIS IS WORKING AGAIN
for (int i = 0; i < versionDates.size(); i++) {
String versionDate = versionDates.get(i);
List<double[]> versionValues = xAndYCoordinates.get(i);
ScatterConfig versionScatterConfig = new ScatterConfig();
XYSeries series = new XYSeries("Version " + (i + 1) + " - "
+ versionDate, versionScatterConfig);
series.setSeriesPoints(getPoints(series, versionValues));
chart.addSeries(series);
}
}
As you can see, the removing and adding of points works perfectly fine, which I assume is because I'm working directly on the chart here, while I'm working on the chartConfig when I try to change the axes caption.
Could you please tell or show me how I can change the caption of the x- and y-Axis in an already existing chart (As described above)?
Thanks a lot
After a lot of research I've come to the conclusion that there seems to be currently no way of changing the x- and yAxis caption dnymically, which works.
I found out that if you refresh the page, e.g. by pressing F5, the axes caption gets changed. I've tried implementing a refresher, but somehow the behaviour still didn't change.
So it looks like a bug (or software failure) for me.
My workaround which is doing the same is just removing the whole chart and then adding a completely new one with the new Axis-caption. This works perfectly fast and fine, but is a dirty solution in my eyes, since you have to add more lines of codes than necessary, as well as the logic is now basically more complicated then it should be.

Has anyone done crosshairs that follow the mouse in JFreeChart?

We are using JFreeChart to make XY plots and we have a feature request to do a crosshair that moves along with the mouse and highlights the data point that most closely maps to the x-value of the mouse. You can see a similar example at Google Finance - http://www.google.com/finance?q=INDEXDJX:.DJI,INDEXSP:.INX,INDEXNASDAQ:.IXIC.
Those Google charts only highlight the current value (we want to do that and also show crosshairs), but they show the live mouse interaction we are looking for.
Anyone have any elegant suggestions?
Thanks.
I got this working using a mouse listener and the CrosshairOverlay class. After I get back from holiday travel, I will post my code. It ended up being not too difficult.
Sorry, I forgot about this!
First, you want to calculate the x, y values for where you want your crosshair. For me, I wanted it to move along the points of our line, so I calculated the closest x value and used that data pair for x, y.
Then I call this method:
protected void setCrosshairLocation(double x, Double y) {
Crosshair domainCrosshair;
List domainCrosshairs = crosshairOverlay.getDomainCrosshairs();
if (domainCrosshairs.isEmpty()) {
domainCrosshair = new Crosshair();
domainCrosshair.setPaint(BlueStripeColors.LIGHT_GRAY_C0);
crosshairOverlay.addDomainCrosshair(domainCrosshair);
}
else {
// We only have one at a time
domainCrosshair = (Crosshair) domainCrosshairs.get(0);
}
domainCrosshair.setValue(x);
if (y != null) {
Crosshair rangeCrosshair;
List rangeCrosshairs = crosshairOverlay.getRangeCrosshairs();
if (rangeCrosshairs.isEmpty()) {
rangeCrosshair = new Crosshair();
rangeCrosshair.setPaint(BlueStripeColors.LIGHT_GRAY_C0);
crosshairOverlay.addRangeCrosshair(rangeCrosshair);
}
else {
// We only have one at a time
rangeCrosshair = (Crosshair) rangeCrosshairs.get(0);
}
rangeCrosshair.setValue(y);
}
}
Note that crosshairOverlay is an instance of CrosshairOverlay.
JFreeChart can't render a sub-section of a chart, so you'll want to do something that doesn't require repainting the chart. You could write your chart to a BufferedImage and store that in memory, then have a custom component which uses the buffered chart as the background image, and draws crosshairs and other popup windows over it.
There are methods in JFreeChart to get the data point for a given coordinate on a rendered chart. Don't recall what these are off the top of my head. Depending on your needs, you might consider rendering your own chart data, it's not as hard as you'd think.
The first thing that comes to my mind would be to write a custom Cursor and set it on your chart. It can have a reference to the chart and highlight the x value that's consistent with the Cursor's x/y location.
This worked for me. I set the
chartPanel.addChartMouseListener(new ChartMouseListener() {
public void chartMouseMoved(ChartMouseEvent event)
{
try
{
double[] values = getCrossHairValue(event);
plot.clearRangeMarkers();
plot.clearDomainMarkers();
Marker yMarker = new ValueMarker(values[1]);
yMarker.setPaint(Color.darkGray);
plot.addRangeMarker(yMarker);
Marker xMarker = new ValueMarker(values[0]);
xMarker.setPaint(Color.darkGray);
plot.addDomainMarker(xMarker);
chartPanel.repaint();
} catch (Exception e)
{
}
}
}

Categories