I'm trying to plot the candles without them overlapping eachother like in the added image.
I played around a bit with the domain range but i'm having trouble figuring out how to keep/set a minimum spacing between the candles.
If someone could point me in the right direction it would be greatly appreciated.
private DateAxis getTimeAxis() {
final DateAxis dateAxis = new DateAxis("TIME_AXIS");
dateAxis.setDateFormatOverride(DATE_TIME_FORMAT);
// this limits the visible candles but doesnt really solve the problem
// long start= ohlcs_series.getX(0,200).longValue(); // start from index 200
// long end = ohlcs_series.getX(0,607).longValue(); // last candle
//
// dateAxis.setRange(
// new Date(start),
// new Date(end + calculateEmptySpace())
// );
dateAxis.setLowerMargin(-0.025);
dateAxis.setUpperMargin(0.15);
Font bold = dateAxis.getLabelFont().deriveFont(Font.BOLD);
dateAxis.setLabelFont(bold);
return dateAxis;
}
/**
* Returns the width of 50 candles
* This is used to create some empty space after the last candle
* */
private long calculateEmptySpace(){
int indexOfLast = ohlcs_series.getSeries(0).getItemCount()-1;
long lastCandle = ohlcs_series.getX(0, indexOfLast).longValue();
long candle= ohlcs_series.getX(0,indexOfLast-50).longValue();
return lastCandle-candle;
}
Related
I am using android java MPAndroidChart Library.
As you can see from photo, value of x axis showing more than one. I just want to show tuesday one time. How can I do it
image of chart
I find the solution
lets say your name of the list is myArray and answer is
barChart.getXAxis().setLabelCount(myArray.size());
You can set a count of label by force. This will result into one label only on your axis.
For example:
xAxis.setLabelCount(1, true)
From the documentation:
/**
* sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware
* that this number is not
* fixed (if force == false) and can only be approximated.
*
* #param count the number of y-axis labels that should be displayed
* #param force if enabled, the set label count will be forced, meaning that the exact
* specified count of labels will
* be drawn and evenly distributed alongside the axis - this might cause labels
* to have uneven values
*/
public void setLabelCount(int count, boolean force) {
setLabelCount(count);
mForceLabels = force;
}
The other thing you can try is working more with granularity.
Like so:
axisLeft.granularity = 3F
From the docs:
/**
* Set a minimum interval for the axis when zooming in. The axis is not allowed to go below
* that limit. This can be used to avoid label duplicating when zooming in.
*
* #param granularity
*/
public void setGranularity(float granularity) {
mGranularity = granularity;
// set this to true if it was disabled, as it makes no sense to call this method with granularity disabled
mGranularityEnabled = true;
}
I want to have x axis labels on my linechart (using MPAndroidChart) but no matter what I do, I can't get them to display. I have a method called setupChart which handles everything for that chart and this is what it looks like:
private void setupChart(LineChart chart, LineData data, int color) {
((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(Color.WHITE);
((LineDataSet) data.getDataSetByIndex(0)).setCircleColor(color);
((LineDataSet) data.getDataSetByIndex(0)).setColors(dataColors);
data.getDataSetByIndex(0).setAxisDependency(YAxis.AxisDependency.LEFT);
((LineDataSet) data.getDataSetByIndex(0)).setDrawCircles(false);
// no description text
chart.getDescription().setEnabled(false);
// mChart.setDrawHorizontalGrid(false);
//
// enable / disable grid background
chart.setDrawGridBackground(false);
// enable touch gestures
chart.setTouchEnabled(true);
// disable scaling and dragging
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
chart.setAutoScaleMinMaxEnabled(false);
// if disabled, scaling can be done on x- and y-axis separately
chart.setPinchZoom(false);
chart.setBackgroundColor(Color.parseColor("#dddddd"));
// set custom chart offsets (automatic offset calculation is hereby disabled)
chart.setViewPortOffsets(10, 0, 10, 0);
// add data
chart.setData(data);
// get the legend (only possible after setting data)
Legend l = chart.getLegend();
l.setEnabled(false);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setEnabled(false);
chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
chart.getXAxis().setEnabled(false);
chart.getXAxis().setDrawLabels(true);
chart.getXAxis().setTextColor(context.getResources().getColor(R.color.colorPrimary));
chart.getXAxis().setTypeface(Typeface.SANS_SERIF);
YAxis yAxis = chart.getAxisLeft();
yAxis.setAxisMinimum(0f);
yAxis.setAxisMaximum(100f);
final String[] weeks = new String[52];
for(int i = 0; i < weeks.length; i++) weeks[i] = "Week " + (i+1);
IAxisValueFormatter formatter = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return weeks[(int) value];
}
};
XAxis xAxis = chart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setValueFormatter(formatter);
// animate calls invalidate()...
chart.animateXY(2000,2500);
}
I am not sure what I am doing wrong.
I solved this by increasing my offsets:
chart.setViewPortOffsets(60, 0, 50, 60);
linechart.setExtraBottomOffset(ff)
Modifying the offsets will help you to solve this problem as well.
Adjust the (ff) float values to get desired space.
My chart was not showing x-axis and y-axis. I remove this line the x-axis and y-axis appeared.
when this line exist lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
when I remove this line lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
I'm using an XYBoxAnnotation to demarcate a rectangular area on a JFreeChart. I would like one side of the box to be "open", i.e go out to infinity. I tried setting the value to Double.POSITIVE_INFINITY but this did not seem to work. I also tried setting it to Double.MAX_VALUE, with no luck either. In these cases, the annotation doesn't even show up on the plot at all. And there are no exceptions thrown.
Below is a very simple version of my code in which I generate the XYBoxAnnotation and add it to the plot.
XYBoxAnnotation _axisMarker = new XYBoxAnnotation(xLow, yLow, Double.POSITIVE_INFINITY, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(_axisMarker, Layer.BACKGROUND);
EDIT:
I figured out that the reason the annotation wasn't showing up was because the x value for the annotation was much much larger than the axis scale. For some reason, this causes the annotation to not be visible until you zoom out enough.
Thanks to #trashgod's answer below, I came up with a solution. His answer didn't quite work for me since my plot allows zooming and you could see the edge of the box when you zoomed out.
First, I added a PlotChangeListener to listen for when the plot is zoomed:
// define PlotChangeListener to update the annotation when the plot is zoomed
private PlotChangeListener _zoomListener = new PlotChangeListener() {
#Override
public void plotChanged(PlotChangeEvent plotChangeEvent) {
if (_basisIsotope != null) {
updateAxisMarkers();
}
}
};
Then I created a function to re-draw the annotation based on the new plot bounds:
// function to re-draw the annotation
private void updateAxisMarkers() {
_plot.removeChangeListener(_zoomListener); // remove to prevent triggering infinite loop
// define xLow, yLow and yHigh...
double xHigh = _plot.getDomainAxis().getUpperBound() * 1.1;
XYBoxAnnotation _axisMarker = new = new XYBoxAnnotation(xLow, yLow, xHigh, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(annotation);
_plot.addChangeListener(_zoomListener); // add back
}
Double.MAX_VALUE is too large to scale to the relevant axis, but Double.MAX_VALUE / 2 works as well as any value larger than the upper bound of the axis. A better choice might be a value that exceeds the maximum value of the domain by some margin. The fragment below shades a plot of some Gaussian data with an XYBoxAnnotation that has domain bounds extending from 42 to the maximum domain value + 10%; the range bounds are ±1σ.
XYSeriesCollection dataset = createDataset();
JFreeChart chart = createChart(dataset);
Color color = new Color(0, 0, 255, 63);
double max = dataset.getSeries(0).getMaxX() * 1.1;
XYBoxAnnotation annotation = new XYBoxAnnotation(
42, -1, max, 1, new BasicStroke(1f), color, color);
chart.getXYPlot().getRenderer().addAnnotation(annotation);
I was able to make a Gantt Chart in JavaFX using this answer- Gantt chart from scratch.
Also i was able to add a DateAxis by using this-http://myjavafx.blogspot.com.by/2013/09/javafx-charts-display-date-values-on.html
But right now it is unusable, because current Gantt chart does not handle "length" as a date. So it draws the beginning of the the chart perfectly accurately, but the end of the chart can be anywhere, and if you resize the window with the chart, the end will be even more random.
I am adding new chart with
.add(new XYChart.Data(job.getTime(), machine, new ExtraData( timeLength, "status-red"))
where "timeLength" i set as number of milliseconds. But basicly that does not work, and it can only receive long.Also i cannot use JfreeChart, because i cannot add it FXML which i use.
So how can i get accurate both beginning and the end of each chart?
Thank you.
Add the following function to DateAxis class to calculate the scale factor from milliseconds to visual units.
/**
* #return The scale factor from milliseconds to visual units
*/
public double getScale(){
final double length = getSide().isHorizontal() ? getWidth() : getHeight();
// Get the difference between the max and min date.
double diff = currentUpperBound.get() - currentLowerBound.get();
// Get the actual range of the visible area.
// The minimal date should start at the zero position, that's why we subtract it.
double range = length - getZeroPosition();
return length/diff;
}
Test results
Date startDate=new Date();
long duration = 1000*60*1;//1 hour in milliseconds
series1.getData().add(new XYChart.Data(startDate, machine, new ExtraData(duration, "status-green")));
startDate = new Date(startDate.getTime()+duration);
duration = 1000*60*1;//2 hours in milliseconds
series1.getData().add(new XYChart.Data(startDate, machine, new ExtraData(duration, "status-red")));
screenshot 1
Occasionally I have to display a popup or dialog relative to an existing component (prime example is a date input control with a calendar button beside it).
It worked beautifully for years, but always had the bug that the calendar could partially appear outside the screen (it was hardcoded to appear just to the right of the field). Just nobody ever noticed because there was never a date control at the far right in a window. Well that changed recently with the addition of a new window.
Well then, I thought, lets just fix a windows position (after I positioned it where it should be) to be completely on screen. I wrote a simple utility method to do just that:
public static void correctWindowLocationForScreen(Window window) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
Rectangle screenRect = gc.getBounds();
Rectangle windowRect = window.getBounds();
Rectangle newRect = new Rectangle(windowRect);
if (windowRect.x + windowRect.width > screenRect.x + screenRect.width)
newRect.x = screenRect.x + screenRect.width - windowRect.width;
if (windowRect.y + windowRect.height > screenRect.y + screenRect.height)
newRect.y = screenRect.y + screenRect.height - windowRect.height;
if (windowRect.x < screenRect.x)
newRect.x = screenRect.x;
if (windowRect.y < screenRect.y)
newRect.y = screenRect.y;
if (!newRect.equals(windowRect))
window.setLocation(newRect.x, newRect.y);
}
Problem solved. Or not. I position my window using the on-screen coordinates from the triggering component (the button that makes the calendar appear):
JComponent invoker = ... // passed in from the date field (a JButton)
Window owner = SwingUtilities.getWindowAncestor(invoker);
JDialog dialog = new JDialog(owner);
dialog.setLocation(invoker.getLocationOnScreen());
correctWindowLocationForScreen(dialog);
Havoc breaks out if the "invoker" component is located in a window that spans two screens. Apparently "window.getGraphicsConfiguration()" returns whatever graphic configuration the windows top left corner happens to be in. Thats not necessarily the screen where the date component within the window is located.
So how can I position my dialog properly in this case?
One can iterate over all devices, and find the monitor where the point is in. Then keep to that Rectangle.
See GraphicsEnvironment.getScreenDevices.
This will not use the current Window, but you already found out that a window may be shown in several monitors.
Useful might be Component.getLocationOnScreen.
Ok, here is what I ended up with (a wall of code to handle the odd edge case).
correctWindowLocationForScreen() will reposition a window if it is not completely within the visible screen area (simplest case, its completely on one screen. Hard case, it spans multiple screens). If the window leaves the complete screen area by just one pixel, it is repositioned using the first screen rectangle found. If the window doesn't fit the screen, its positioned at the top left and extends over the screen to bottom right (its implied by the order in which positionInsideRectangle() checks/alters coordinates).
Its quite complicated considering the requirement is pretty simple.
/**
* Check that window is completely on screen, if not correct position.
* Will not ensure the window fits completely onto the screen.
*/
public static void correctWindowLocationForScreen(final Window window) {
correctComponentLocation(window, getScreenRectangles());
}
/**
* Set the component location so that it is completely inside the available
* regions (if possible).
* Although the method will make some effort to place the component
* nicely, it may end up partially outside the regions (either because it
* doesn't fit at all, or the regions are placed badly).
*/
public static void correctComponentLocation(final Component component, final Rectangle ... availableRegions) {
// check the simple cases (component completely inside one region, no regions available)
final Rectangle bounds = component.getBounds();
if (availableRegions == null || availableRegions.length <= 0)
return;
final List<Rectangle> intersecting = new ArrayList<>(3);
for (final Rectangle region : availableRegions) {
if (region.contains(bounds)) {
return;
} else if (region.intersects(bounds)) {
// partial overlap
intersecting.add(region);
}
}
switch (intersecting.size()) {
case 0:
// position component in the first available region
positionInsideRectangle(component, availableRegions[0]);
return;
case 1:
// position component in the only intersecting region
positionInsideRectangle(component, intersecting.get(0));
return;
default:
// uuuh oooh...
break;
}
// build area containing all detected intersections
// and check if the bounds fall completely into the intersection area
final Area area = new Area();
for (final Rectangle region : intersecting) {
final Rectangle2D r2d = new Rectangle2D.Double(region.x, region.y, region.width, region.height);
area.add(new Area(r2d));
}
final Rectangle2D boundsRect = new Rectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height);
if (area.contains(boundsRect))
return;
// bah, just place it in the first intersecting region...
positionInsideRectangle(component, intersecting.get(0));
}
/**
* Position component so that its completely inside the rectangle.
* If the component is larger than the rectangle, component will
* exceed to rectangle bounds to the right and bottom, e.g.
* the component is placed at the rectangles x respectively y.
*/
public static void positionInsideRectangle(final Component component, final Rectangle region) {
final Rectangle bounds = component.getBounds();
int x = bounds.x;
int y = bounds.y;
if (x + bounds.width > region.x + region.width)
x = region.x + region.width - bounds.width;
if (y + bounds.height > region.y + region.height)
y = region.y + region.height - bounds.height;
if (region.x < region.x)
x = region.x;
if (y < region.y)
y = region.y;
if (x != bounds.x || y != bounds.y)
component.setLocation(x, y);
}
/**
* Gets the available display space as an arrays of rectangles
* (there is one rectangle for each screen, if the environment is
* headless the resulting array will be empty).
*/
public static Rectangle[] getScreenRectangles() {
try {
Rectangle[] result;
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice[] devices = ge.getScreenDevices();
result = new Rectangle[devices.length];
for (int i=0; i<devices.length; ++i) {
final GraphicsDevice gd = devices[i];
result[i] = gd.getDefaultConfiguration().getBounds();
}
return result;
} catch (final Exception e) {
return new Rectangle[0];
}
}