Draw mathematical functions in Java using JFreeChart - java

I've been googling my for a while now, and did not find any useful stuff, so that is why I ask you guys.
Can I draw mathematical functions (e.g. sine, cosine, etc.) with JFreeChart?
Thanks

JFreeChart is for plotting data, not functions. But you should be able to create a table of (x,y) values and then plot them. If this is for a desktop app, look at the JavaFX api. The API includes charts and functions to draw lines.

Im assuming that you can plot the points yourself in which case you would simply evaluate the mathematical function for each x along the graph.
getY(float x) {
return /*your function*/ Math.sin(x);
}

There may not be a built in way to plot sinx but there doesn't need to be. Remember that what your saying is y=sin(x)! What you need to plot is the x and y value. Create a loop of x values then plug them into sin(x) using Java and Math. That answer IS your y value! So now you have your x and y values to plot sin(x).
Example
final XYSeries series1 = new XYSeries("First");
for(double i = 0; i < 10; i += 0.2){
double sinx = Math.sin(i);
series1.add(i, sinx);
}
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series1);

Related

How to fix the limit of x axis with GraphView

I want to develop an app which communicates with BLE sensors at 100 Hz and to plot real time data using GraphView. I want to plot data in an "ECG way of doing", by having always the same fixed x axis and the curve which plots during time and come back on the left of the graph if the plot has reached the right end of the graph. I managed to receive data and to plot it but I have two concerns:
Sometimes, it became a bit laggy for like half a second, and I do not know if it comes from my app, or my phone (which is an OnePlus 6, having so quite great performances).
I am not able to "fix" the x axis limits... To try to explain it, the width of the x axis is fixed, but the curve starts plotting on the right part of the graph and fills the graph in the left direction until the curve has touched the left border of the graph. However, this is not what I wanted to do.
Here is some code snippets to help you understand what I have done wrong.
LineGraphSeries<DataPoint> series;
private int time = 0;
private int maxDataPoints = 200;
In OnCreate function
mDataGraph = (GraphView) findViewById(R.id.graph_data);
mDataGraph.getGridLabelRenderer().setHorizontalLabelsVisible(false);
mDataGraph.getGridLabelRenderer().setVerticalLabelsVisible(false);
mDataGraph.getGridLabelRenderer().setHighlightZeroLines(true);
mDataGraph.getGridLabelRenderer().setGridStyle(GridLabelRenderer.GridStyle.NONE);
mDataGraph.getViewport().setDrawBorder(true);
mDataGraph.setKeepScreenOn(true);
series = new LineGraphSeries<DataPoint>();
mDataGraph.addSeries(series);
// customize a little bit viewport
Viewport viewport = mDataGraph.getViewport();
viewport.setXAxisBoundsManual(true);
viewport.setMinX(0);
viewport.setMaxX(maxDataPoints);
viewport.setScalable(true);
viewport.setScrollable(true);
My function to handle data texts and plots
private void displayData(String data) {
if (data != null) {
mDataField.setText(data);
int dataNumber = Integer.parseInt(data);
// Reset graph
if (time == maxDataPoints){
mDataGraph.removeAllSeries();
series = new LineGraphSeries<DataPoint>();
mDataGraph.addSeries(series);
time = 0;
}
series.appendData(new DataPoint(time, dataNumber), true, maxDataPoints);
time ++;
}
}
Note that my template is the Bluetooth Le Gatt one.
It is the first time I use this forum and I code in android studio (and even in java), so I hope that my explanations are clear and that someone can help me.
Thanks to take time to read this.
Regards,
Arthur.
I was able to fix it by setting scrollToEnd to false in the appendData function...
Was so easy.
Regards,
Arthur.

how to find x value based on y

I am creating a graph with contain x and y value. The y axis in normal but x is with the logarithmic value. I used the library (jfreechart.jar) for making this logarithmic graph.
My question is how can in fine the exact value of x if for example y is 10? (the value of x is in logarithmic number)
private void interActionPerformed(java.awt.event.ActionEvent evt) {
final XYSeries s1 = new XYSeries("Series 1");
s1.add(0.075,4.8);
s1.add(0.15,13.9);
s1.add(0.425,19.5);
s1.add(0.6,22.1);
s1.add(1.18,26.6);
s1.add(2,29.5);
s1.add(2.36,31.2);
s1.add(4.75,38.6);
s1.add(9.5,46.2);
s1.add(19,62.4);
s1.add(25,76.);
s1.add(37.5,86.2);
s1.add(50,100);
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(s1);
final JFreeChart chart = ChartFactory.createXYLineChart(
"sieve analyese", // chart title
"sieve size", // domain axis label
"passing", // range axis label
dataset, // data
PlotOrientation.VERTICAL,
true, // include legend
true,
false
);
final XYPlot plot = chart.getXYPlot();
final LogarithmicAxis domainAxis = new LogarithmicAxis("particle size in milimeters");
final NumberAxis rangeAxis = new NumberAxis("percent passing");
plot.setDomainAxis(domainAxis);
plot.setRangeAxis(rangeAxis);
BarRenderer renderer = null;
ChartFrame frame = new ChartFrame("sive chart", chart);
frame.setVisible(true);
frame.setSize(1000,600);
chart.setBackgroundPaint(Color.lightGray);
plot.setOutlinePaint(Color.RED);
} [chart of x and y value][1]
Use the below function to get the value of x from y
x = Math.log(y)
For exact value of X you should know exact dependence (function) of Y(X) or X(Y). For real-life data the kind of this function often is unknown, and one have to apply some approximation method.
Considering that your data are monotone (increasing), you can find an index of interval where given Y lie using binary search. For sample data you'll find 0-th interval between (0.075,4.8) and (0.15,13.9)
Then apply some interpolation method. The simplest approach - linear interpolation.
For you data set you might use quadratic interpolation with better accuracy - use 3 closest points to build parabola equation, and from this equation get X for given Y.

JFreeChart BarChart - Category Markers

I'm creating a BarChart that contains multiple frequency bands as the category axis. What I want to do is to show a visible grouping of these frequency bands:
For example:
Freq x1 ~ Freq x2 = Band y (so the domain axis has values for category x1, x1.1, x1.2 till x2)
Freq x3 ~ Freq x4 = Band z (x3, x3.1 .....x4)
What I want to do is show markers for Band Y and Band Z in the graph. Note that based on the dataset that I may get, not all categories may be present. Say, for the 1st example, I've got values for x1 to x1.6 and so the band marker would be from x1 till x1.6
I hope I could explain my requirement. Is this possible in JFreeChart? If so, how may I go about achieving this?
Just to clarify a little more, here's a picture of something that I want to achieve:
Do your Bands corresponded to Categories? If they do you can use a CategoryMarker
CategoryMarker marker = new CategoryMarker("Category 3");
marker.setLabel("Band Y");
marker.setPaint(Color.red);
marker.setOutlinePaint(Color.red);
marker.setAlpha(0.5f);
marker.setLabelAnchor(RectangleAnchor.TOP);
marker.setLabelTextAnchor(TextAnchor.TOP_CENTER);
marker.setLabelOffsetType(LengthAdjustmentType.CONTRACT);
plot.addDomainMarker(marker, Layer.BACKGROUND);
I can't work out how to create a Mutli-CategoryMarker but you can create something similer by
adjusting the ItemMargin and CategoryMargin and adding additional CategoryMarkers
{
CategoryMarker marker = new CategoryMarker("Category 2");
marker.setLabel("Band X");
marker.setLabelAnchor(RectangleAnchor.TOP);
marker.setLabelTextAnchor(TextAnchor.TOP_CENTER);
marker.setLabelOffsetType(LengthAdjustmentType.CONTRACT);
plot.addDomainMarker(marker, Layer.BACKGROUND);
}
{
CategoryMarker marker = new CategoryMarker("Category 3");
plot.addDomainMarker(marker, Layer.BACKGROUND);
}
renderer.setItemMargin(0.0);
CategoryAxis axis = plot.getDomainAxis();
axis.setCategoryMargin(0);
}
You could create a method to add multiple markers e.g
private void addMarkers(List<Comparable> keys){
...
A more correct solution may be to write your own implementation of the Renderer and accociated code

An exact method of area calculation using UTM coordinates

I have a list of lat/long coordinates that I would like to use to calculate an area of a polygon. I can get exact in many cases, but the larger the polygon gets, the higher chance for error.
I am first converting the coordinates to UTM using http://www.ibm.com/developerworks/java/library/j-coordconvert/
From there, I am using http://www.mathopenref.com/coordpolygonarea2.html to calculate the area of the UTM coordinates.
private Double polygonArea(int[] x, int[] y) {
Double area = 0.0;
int j = x.length-1;
for(int i = 0; i < x.length; i++) {
area = area + (x[j]+x[i]) * (y[j]-y[i]);
j = i;
}
area = area/2;
if (area < 0)
area = area * -1;
return area;
}
I compare these areas to the same coordinates I put into Microsoft SQL server and ArcGIS, but I cannot seem to match them exactly all the time. Does anyone know of a more exact method than this?
Thanks in advance.
EDIT 1
Thank you for the comments.
Here is my code for getting the area (CoordinateConversion code is listed above on the IBM link):
private Map<Integer, GeoPoint> vertices;
private Double getArea() {
List<Integer> xpoints = new ArrayList<Integer>();
List<Integer> ypoints = new ArrayList<Integer>();
CoordinateConversion cc = new CoordinateConversion();
for(Entry<Integer, GeoPoint> itm : vertices.entrySet()) {
GeoPoint pnt = itm.getValue();
String temp = cc.latLon2MGRUTM(pnt.getLatitudeE6()/1E6, pnt.getLongitudeE6()/1E6);
// Example return from CC: 02CNR0634657742
String easting = temp.substring(5, 10);
String northing = temp.substring(10, 15);
xpoints.add(Integer.parseInt(easting));
ypoints.add(Integer.parseInt(northing));
}
int[] x = toIntArray(xpoints);
int[] y = toIntArray(ypoints);
return polygonArea(x,y);
}
Here is an example list of points:
44.80016800 -106.40808100
44.80016800 -106.72123800
44.75016800 -106.72123800
44.75016800 -106.80123800
44.56699100 -106.80123800
In ArcGIS and MS SQL server I get 90847.0 Acres.
Using the code above I get 90817.4 Acres.
Another example list of points:
45.78412600 -108.51506700
45.78402600 -108.67972100
45.75512200 -108.67949400
45.75512200 -108.69962300
45.69795400 -108.69929400
In ArcGIS and MS SQL server I get 15732.9 Acres.
Using the code above I get 15731.9 Acres.
The area formula you are using is valid only on a flat plane. As the polygon gets larger, the Earth's curvature starts to have an effect, making the area larger than what you calculate with this formula. You need to find a formula that works on a the surface of a sphere.
A simple Google search for "area of polygon on spherical surface" turns up a bunch of hits, of which the most interesting is Wolfram MathWorld Spherical Polygon
It turns out that UTM just isn't able to get the extreme accuracy I was looking for. Switching projection systems to something more accurate like Albers or State Plane provided a much more accurate calculation.

Creating an area graph below a XYDifference(Renderer) graph

I have been trying for the last week to find a way to make JFreeChart display something similar to the image below. Basically you are looking at three series (upper, middle, lower) with a fill inbetween. And underneath there is a (light green) fill color, or an area chart as some would perhaps call it - no meaning, just for looks.
The only thing really missing from what I have come up with is the last part: the fill underneath / area chart:
I even tried to subclass XYDifferenceRenderer and combine it with the renderer for Areachart, but I could not control the height of the areachart, basically filling up the plot to the top. So that was a no-go. Having created as simple rendererer to create rounded bar charts earlier, I thought that I might be able to change the code for XYDifferenceRenderer. But the code for XYDifferenceRenderer is quite a handful of geometry and inner workings of JFree chart, and the task was a bit overwhelming. So any tips on how to achieve this effect in any "normal" way (that does not involve hacking JFreeChart's inner workings)?
Found an old post describing how to use two renderers in the same plot, which was just the thing in this case.
To get a fill underneath you need to
create two new series
one is the lower bound of the difference plot
the other is the values at the bottom of the plot - often just zero. Easily got by calling plot.getRangeAxis().getLowerBound()
add them to a new dataset and add this to the plot
I was unaware that a plot could have several datasets. Turns out one can just use an index to access them.
create a new renderer for the "fill" dataset
create a new renderer
set the right fill paint
set the rendererer for the new dataset to be the new renderer
The code is something akin to the following, where the fill Paint obviously is up to you:
static void addFill(Plot plot) {
XYSeries lowerLimitSeries = ((XYSeriesCollection) (plot.getDataset())).getSeries(1);
XYSeriesCollection fillSet = new XYSeriesCollection();
double lowerBound = plot.getRangeAxis().getLowerBound();
fillSet.addSeries(lowerLimitSeries);
fillSet.addSeries(createLowerFillSeries(lowerLimitSeries, lowerBound));
plot.setDataset(1, fillSet);
Paint fillPaint = Color.GREEN;
XYDifferenceRenderer fillRenderer = new XYDifferenceRenderer(fillPaint, fillPaint, false);
fillRenderer.setSeriesStroke(0, new BasicStroke(0)); //do not show
fillRenderer.setSeriesStroke(1, new BasicStroke(0)); //do not show
plot.setRenderer(1, fillRenderer);
...
}
static XYSeries createLowerFillSeries(XYSeries lowerLimitSeries, double lowerLimit) {
int size = lowerLimitSeries.getItems().size();
XYSeries res = new XYSeries("lowerFillSeries");
for (int i = 0; i < size; i++) res.add(new XYDataItem(lowerLimitSeries.getX(i), lowerLimit));
return res;
}

Categories

Resources