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

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.

Related

Limit JFreeChart TimeSeries to Business Hours

Rendering a chart over several days, with a dataset that has 24 hour data, but it's only useful during M-F, 7AM to 5PM. If I setup a time series with the code below, I get a chart that contains all 24 hours, 7 days a week. Makes sense, but not for my use case.
Is there a way to define what interval the time series displays? Or do I need to use a different chart type and attempt to fit my data into regular periods? I hope not the latter, while the data I receive is usually in 30 second intervals, there can easily be gaps.
It's pretty impossible to post an SSCE of a working UI with a chart dynamically asking for data from a server, but some highlights are below to get an idea of the chart types I'm using.
Some of the plot.add, CombinedDomainXY, index 0 code may seem strange. I have three subplots with the shared time values, I've pared it down to one here to keep it short. I assume there is a way to do what I need for one plot it would work for a chart with multiple subplots.
public ChartPanel extends JPanel
{
private final MyDataset _myDataset = new MyDataset();
private final XYPlot _myPlot = new XYPlot();
_chartPanel = new ChartPanel( createChart() );
private JFreeChart createChart()
{
CombinedDomainXYPlot plot = new CombinedDomainXYPlot(
timeAxis );
plot.setGap( 10.0 );
plot.setDomainPannable( true );
plot.setDataset( index, dataset );
NumberAxis axis = new NumberAxis();
axis.setAutoRangeIncludesZero( false );
plot.setRangeAxis( 0, axis );
plot.setRangeAxisLocation( 0, axisLocation );
plot.setRenderer( 0, new StandardXYItemRenderer() );
plot.mapDatasetToRangeAxis( 0, index );
// add the subplots...
plot.add( _myPlot, 1 );
}
}
public class MyDataset implements XYDataset
{
#Override
public double getYValue( int series, int item )
{
return getMyData(item);
}
#Override
public double getXValue( int series, int item )
{
return _bars.get( item ).DateTime.toInstant().toEpochMilli();
}
//other basic overloaded methods left out for brevity
}
You may be able to use a DateAxis with a custom Timeline. SegmentedTimeline, examined here, is a concrete implementation; although deprecated, it may serve as a guide. Based on this example, your notional newWorkdayTimeline() might look something like this:
public static SegmentedTimeline newWorkdayTimeline() {
SegmentedTimeline timeline = new SegmentedTimeline(
SegmentedTimeline.HOUR_SEGMENT_SIZE, 10, 14);
timeline.setStartTime(SegmentedTimeline.firstMondayAfter1900()
+ 7 * timeline.getSegmentSize());
timeline.setBaseTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
return timeline;
}
This example illustrates one way to mitigate any rendering artifacts you encounter.

libgdx update label text and position simultaniously

What I'm trying to do is add a label to follow a slider knob and display the knob's value. I currently have a vertical slider and a label stacked inside a table cell. I'm able to position the label to where the knob is and update it's text correctly but am having a problem while moving the knob.
When I move the knob I can make the label follow it as long as I don't update the text. But if I update the text then the label re-centers itself in the cell till the knob has stopped moving at which point it places itself on the knob.
How do I make it so that the label doesn't reposition while it's updating text?
Here's some sample code of what I have going on:
public class OptionsWindow implements Screen {
private Stage stage;
private Table table;
private Slider slider;
private Label label;
private Skin textSkin = new Skin(Gdx.files.internal("skins/skin.json"),
new TextureAtlas(Gdx.files.internal("skins/text.pack")));
private Skin sliderSkin = new Skin(Gdx.files.internal("skins/skin.json"),
new TextureAtlas(Gdx.files.internal("skins/text.pack")));
private min = 1, max = 2;
#Override
public void show() {
stage = new Stage();
table = new Table();
slider = new Slider(min, max, 0.001f, true, sliderSkin);
label = new Label(""), textSkin);
table.stack(label, slider).height(1000);
table.setFillParent(true);
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
label.setY(slider.getValue() - min);
label.setText(slider.getValue());
stage.act();
stage.draw();
}
}
I've done a lot of searching for an answer to this question but have yielded no results. I have also tried placing the label in a container and moving the container while changing the label text, but the same result happens. I'm sure somebody had done this before. It seems like something people would want. Please, any help is welcome.
Never done it before, I always show the amount in a label next to or above the slider to give feedback. But the result you are after gives a nice touch. Did you try adding a ChangeListener to your slider? Everytime the slider changes this would be called. So if you insert the new position and text in there it should be updating correctly.
//Slider has to be "final" for the listener.
final Slider slider = new Slider(0, 10, 1, true, skin);
slider.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
label.setY(slider.getValue() - min);
label.setText(slider.getValue());
}
});
This does not need to be in your render method. It's a listener so whenever something happens the listener listens for it will execute.
Ok, so I came up with a work around. It would seem that labels move back to their cell alignment position while updating text. to solve this I simply placed my label in a table with a row on either side like so:
Table subTable = new Table();
subTable.add().row();
subTable.add(label).row();
subTable.add();
Then in render I calculate new height values for the exterior cells and apply them:
float topH, botH;
// Calculate heights
topH = (1 - slider.getPercent()) * parentTable.getHeight();
botH = slider.getPercent() * parentTable.getHeight();
// Apply heights to exterior table cells
subTable.getCells().get(0).height(topH);
subTable.getCells().get(2).height(botH);
// Update label text
label.setText(slider.getValue());
This is just a simple form of what I have. You'll want to take into account the label height to get it to position correctly. Also if you want it to follow with the slider at the slider's set animateDuration, you'll want to use getVisualPercent() instead of getPercent(). However this causes issues when your finger flies out of the cell. For some reason the visual percent is linked to where the slider knob is when your finger leaves the cell, not where it ends up.
My work around for this was to just set the animateDuration to 0.0f and just use getPercent(). If I or someone comes up with a solution to the getVisualPercent() option, then that would be nice to have posted as well.

Making Standard Dial Range on JFreeChart thicker

I made a dial chart Using JFreeChart. I was wondering if it was possible to make the indicators thicker. The code I am using to make them now are:
StandardDialRange standarddialrange;
StandardDialRange standarddialrange2;
StandardDialRange standarddialrange3;
if(isPercentageIV==true){
standarddialrange = new StandardDialRange(90D, 100D, Color.GREEN);
standarddialrange2 = new StandardDialRange(60D, 90D, Color.orange);
standarddialrange3 = new StandardDialRange(0D, 60D, Color.RED);
}
else{
standarddialrange = new StandardDialRange(.9*goal*dialScale, goal*dialScale, Color.GREEN);
standarddialrange2 = new StandardDialRange(.6*goal*dialScale, .9*goal*dialScale, Color.orange);
standarddialrange3 = new StandardDialRange(0, .6*goal*dialScale, Color.RED);
}
// Sets the scale/radius of all the indicators.
standarddialrange.setScaleIndex(0);
standarddialrange.setInnerRadius(0.58999999999999997D);
standarddialrange.setOuterRadius(0.58999999999999997D);
dialplot.addLayer(standarddialrange);
standarddialrange2.setScaleIndex(0);
standarddialrange2.setInnerRadius(0.58999999999999997D);
standarddialrange2.setOuterRadius(0.58999999999999997D);
dialplot.addLayer(standarddialrange2);
standarddialrange3.setScaleIndex(0);
standarddialrange3.setInnerRadius(0.58999999999999997D);
standarddialrange3.setOuterRadius(0.58999999999999997D);
dialplot.addLayer(standarddialrange3);
I tried looking online and I could not figure out how to make it thicker. The way they are now makes them kind of hard to see on a display from far away. I tried changing the outer radius but it just made it so their were two thin lines, instead of one big thick one.
Override the draw() method of StandardDialRange and specify your preferred Stroke; I've used 4.0f in the example below. You'll need to recapitulate the existing code, using the public accessors as required.
plot.addLayer(new StandardDialRange(3 * maximumValue / 4, maximumValue, Color.red) {
#Override
public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) {
…
g2.setPaint(this.getPaint());
g2.setStroke(new BasicStroke(4.0f));
g2.draw(arcInner);
g2.draw(arcOuter);
}
});

Bad text alignment in SWT Label

I am trying to do something that should be absolutely trivial, namely add a product version number to an RCP application's splash screen. So my code goes like this:
class ARMSplashHandler extends EclipseSplashHandler {
private Font font;
private int heightInPx = 12;
private int fontHeightInPt(Display display, int heightInPx) {
return 72 * heightInPx / display.getDPI().y;
}
#Override void init(Shell splash) {
Product product = Platform.getProduct();
Display display = splash.getDisplay();
FontData fontData = splash.getFont.getFontData()[0];
fontData.setHeight(fontHeightInPt(display, fontHeightInPx));
font = new Font(display, fontData);
splash.setBackgroundMode(SWT.INHERIT_DEFAULT);
splash.setText(product.getName());
splash.setFont(font);
String version = "v"+product.getDefiningBundle().getHeaders().get("Bundle-Version");
String versionLocString = product.getProperty("versionLocation");
String versionColorString = product.getProperty("versionColor");
Rectangle versionLoc = StringConverter.asRectangle(versionLocString, new Rectangle(405, 260, 45, 12));
Color versionColor = new Color(display, StringConverter.asRGB(versionColorString, new RGB(193, 202, 212)));
CLabel versionLabel = new CLabel(splash, SWT.CENTER | SWT.BORDER);
versionLabel.setForeground(versionColor);
versionLabel.setFont(font);
versionLabel.setBounds(versionLoc);
versionLabel.setText(version);
splash.addDisposeListener(new DisposeListener {
void widgetDisposed(DisposeEvent e) {
versionColor.dispose();
font.dispose();
}
});
super.init(splash);
}
This works fine (except for a problem on Mac I'll ask about in a separate question; of course, the border is just for debugging):
But if I replace CLabel with Label, this happens:
It isn't centered, as expected. If I set alignment to RIGHT (which is what I really want), it disappears completely. Initially I thought the problem simply was that there isn't enough space, but increasing height of the rectangle doesn't fix the problem, and the width is certainly large enough:
Is this somehow expected behavior? Or an SWT bug (I certainly don't expect there to be one in something this basic)?

jChart2D - Colors for TracePoint2D

Given the following code:
chart = new Chart2D();
trace = new Trace2DSimple();
trace.setTracePainter(new TracePainterVerticalBar(chart));
chart.addTrace(trace);
// default tracepainter color is Black
TracePoint2D first = new TracePoint2D(0, 1 );
TracePoint2D second = new TracePoint2D(1, 10 );
TracePoint2D third = new TracePoint2D(2, 20 );
PointPainterVerticalBar red = new PointPainterVerticalBar(10,chart);
red.setColor(Color.red);
PointPainterVerticalBar green = new PointPainterVerticalBar(10,chart);
green.setColor(Color.green);
PointPainterVerticalBar blue = new PointPainterVerticalBar(10,chart);
blue.setColor(Color.blue);
first.addAdditionalPointPainter(red);
second.addAdditionalPointPainter(green);
third.addAdditionalPointPainter(blue);
trace.addPoint(first);
trace.addPoint(second);
trace.addPoint(third);
As you can see I am trying to get three different TracePoints with different colors onto the chart but somehow they all remain black. Any Ideas ?
Well it is perfectly possible just implement you own Trace- and PointPainter.
Within the new TracePainter tell it to use your new PointPainter:
public TracePainterBlank(final int barWidth, final Chart2D chart) {
this.m_pointPainter = new PointPainterBlank(barWidth, chart);
}
Then within the PointPainer be explicit about the color which you set with tracepainter.setColor(Color.whatever) and apply it to the actual graphics:
public void paintPoint(final int absoluteX, final int absoluteY, final int nextX,
final int nextY, final Graphics g, final ITracePoint2D original) {
g.setColor(this.getColor());
g.fillRect(absoluteX - this.m_halfWidth, absoluteY, 2 * this.m_halfWidth, this.m_chart
.getYChartStart()
- absoluteY);
}
That lets you color each TracePoint separately.
I think with jChart2D you can only set Color for a "trace" and not colors of individual points.
your code is buggy as you set the color only on red three times.
But as it remains black as you write I fear there is a bug (that code was changed recently). So consider posting a bug at sourceforge if it is the case.
HTH,
Achim

Categories