I want to know how to draw lines, dots(or tiny circles), rectangles and trapezoids in jfreechart on a XY Plot. Most of them from certain coordinate to the Range Zero Baseline. I have to represent root-finding methods like this:
http://www2.lv.psu.edu/ojj/courses/cmpsc-201/201-images/bisect.jpg
or Simpson's rule like this:
http://upload.wikimedia.org/wikipedia/commons/0/08/Simpson_rule.png
I already have the functions for to find the solution and to plot, and I only need draw the shapes in certain coordinates. I'm new with jfreechart and plotting and I was been looking for ways to do this.
My plot's code:
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.plot.XYPlot;
public class Grafica1 {
JFreeChart grafica;
XYSeriesCollection datos=new XYSeriesCollection();
String titulo;
String etiquetax;
String etiquetay;
int cont=1;
public Grafica1(String t, String x, String y){
titulo=t;
etiquetax=x;
etiquetay=y;
grafica=ChartFactory.createXYLineChart(titulo, x, y, datos, PlotOrientation.VERTICAL, true, true, true);
XYPlot plot= (XYPlot) grafica.getPlot();
//plot.setDomainZeroBaselineVisible(true);
plot.setRangeZeroBaselineVisible(true);
}
public Grafica1(){
this("Sin título", "x", "y");
}
public void agregarGrafica(String id, double[] x, double[] y){
XYSeries s=new XYSeries("["+(cont++)+"] "+id);
int n=x.length;
for(int i=0;i<n;i++){
s.add(x[i], y[i]);
}
datos.addSeries(s);
}
public void crearGrafica(String id, double[] x, double[] y){
cont=1;
datos.removeAllSeries();
agregarGrafica(id, x, y);
}
public JPanel obtieneGrafica(){
return new ChartPanel(grafica);
}
}
You can combine XYLineAndShapeRenderer with SwingWorker, as shown here. Use annotations and markers as required.
Related
I have two streams. First one is time-based stream and I used the countTimeWindow to receive first 10 data points for calculating stat value. I manually used the variable cnt to only keep the first window and filtered the remaining values as shown in the below code.
And then, I want to use this value to filter the main stream in order to have the values which is greater than the stat value that I computed in the window stream.
However, I don't have any idea how to merge or calculate these two streams for achieving my goal.
My scenario is that if I convert the first stat value into the broadcast variable, then I give it to the main stream so that I am able to filter the in-coming values based on the stat value in the broadcast variable.
Below is my code.
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.GlobalWindow;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer09;
import org.apache.flink.streaming.util.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.functions.windowing.*;
import org.apache.flink.util.Collector;
import scala.Int;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class ReadFromKafka {
static int cnt = 0;
public static void main(String[] args) throws Exception{
// create execution environment
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "localhost:9092");
properties.setProperty("group.id", "flink");
DataStream<String> stream = env
.addSource(new FlinkKafkaConsumer09<>("flinkStreaming11", new SimpleStringSchema(), properties));
env.enableCheckpointing(1000);
//Time based window stream
DataStream<String> process = stream.countWindowAll(10).process(new ProcessAllWindowFunction<String, Tuple2<Double, Integer>, GlobalWindow>() {
#Override
public void process(Context context, Iterable<String> iterable, Collector<Tuple2<Double, Integer>> collector) throws Exception {
Double sum = 0.0;
int n = 0;
List<Double> listDouble = new ArrayList<>();
for (String in : iterable) {
n++;
double d = Double.parseDouble(in);
sum += d;
listDouble.add(d);
}
cnt++;
Double[] sd = listDouble.toArray(new Double[listDouble.size()]);
double mean = sum / n;
double sdev = 0;
for (int i = 0; i < sd.length; ++i) {
sdev += ((sd[i] - mean) * (sd[i] - mean)) / (sd.length - 1);
}
double standardDeviation = Math.sqrt(sdev);
collector.collect(new Tuple2<Double, Integer>(mean + 3 * standardDeviation, cnt));
}
}).filter(new FilterFunction<Tuple2<Double, Integer>>() {
#Override
public boolean filter(Tuple2<Double, Integer> doubleIntegerTuple2) throws Exception {
Integer i1 = doubleIntegerTuple2.f1;
if (i1 > 1)
return false;
else
return true;
}
}).map(new RichMapFunction<Tuple2<Double, Integer>, String>() {
#Override
public String map(Tuple2<Double, Integer> doubleIntegerTuple2) throws Exception {
return String.valueOf(doubleIntegerTuple2.f0);
}
});
//I don't think that this is not a proper solution.
process.union(stream).filter(new FilterFunction<String>() {
#Override
public boolean filter(String s) throws Exception {
return false;
}
})
env.execute("InfluxDB Sink Example");
env.execute();
}
}
First, I think you only have one stream, right? There's only one Kafka-based source of doubles (encoded as Strings).
Second, if the first 10 values really do permanently define the limit for filtering, then you can just run the stream into a RichFlatMap function, where you capture the first 10 values to calculate your max value, and then filter all subsequent values (only output values >= this limit).
Note that typically you'd want to save state (array of 10 initial values, plus the limit) so that your workflow can be restarted from a checkpoint/savepoint.
If instead you are constantly re-calculating your limit from the most recent 10 values, then the code is just a bit more complex, in that you have a queue of values, and you need to do the filtering on the value being flushed from the queue when you add a new value.
This question already has answers here:
Making a Scatter Plot Using 2d array and JfreeChart
(1 answer)
Setting Range for X,Y Axis-JfreeChart
(2 answers)
Changing the shapes of points in scatter plot
(2 answers)
Adding points to XYSeries dynamically with JfreeChart
(1 answer)
Closed 5 years ago.
How to plot line graph using JFreeChart with two arrays: one for x coordinates and the other for y coordinates. I have two functions which gives me two arrays. I want to plot line graph with that arrays is there any possible way that I can do this.
XYSeries series = new XYSeries(" ");
for(int i=((fIndex-1)*2);i<=((tIndex*2)-1);i+=2)
{
series.add(xTValue[i],yTValue[i]);
}
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series);
return dataset;
Can I do this as above code.
You can use
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series = new DefaultXYSeries();
series.addSeries(" ", new double[][]{xTValue,yTValue});
dataset.addSeries(series);
JFreeChart chart = ChartFactory.createXYLineChart("My chart title",
"my x-axis label", "my y-axis label", dataset);
Alternatively, if you dont specifically need an XYLineChart, then you can do this by subclassing FastScatterPlot (I know you want a line plot, but this is an easy way in - you override the render() method to draw lines!), with something like the following:
public class LinePlot extends FastScatterPlot {
private float[] x, y;
public LinePlot(float[] x, float[] y){
super();
this.x=x;
this.y=y;
}
#Override
public void render(final Graphics2D g2, final Rectangle2D dataArea,
final PlotRenderingInfo info, final CrosshairState crosshairState) {
// Get the axes
ValueAxis xAxis = getDomainAxis();
ValueAxis yAxis = getRangeAxis();
// Move to the first datapoint
Path2D line = new Path2D.Double();
line.moveTo(xAxis.valueToJava2D(x[0], dataArea, RectangleEdge.BOTTOM),
yAxis.valueToJava2D(y[0], dataArea, RectangleEdge.LEFT));
for (int i = 1; i<x.length; i++){
//Draw line to next datapoint
line.lineTo(aAxis.valueToJava2D(x[i], dataArea, RectangleEdge.BOTTOM),
yAxis.valueToJava2D(y[i], dataArea, RectangleEdge.LEFT));
}
g2.draw(line);
}
}
This is a fairly bare-bones implementation - for example no checks that x and y have same number of points, and no addition of colour etc (e.g. g2.setPaint(myPaintColour) or line type (e.g. g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER, 10.0f, new float[] { 7, 3, 1, 3 }, 0.0f)) would give alternating -•-•-type lines)
I am trying to merge multiple graphs to create a single chart. The individual graphs have White background but somehow my merged chart end up getting gray background despite using setBackgroundpaint api.
public static String mergeXYGraphs(List<XYPlot> plots, String title, boolean legend, int width, int height) throws IOException
{
if(plots != null && !plots.isEmpty())
{
XYPlot base = plots.get(0);
for(int i = 1; i< plots.size(); i++)
{
base.setDataset(i, plots.get(i).getDataset());
base.setRenderer(i, plots.get(i).getRenderer());
}
JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, base, legend);
setDateAxis(base);
chart.getXYPlot().setBackgroundPaint(Color.WHITE);
return saveImageFile(chart, "merged", "charts", width, height);
}
return "";
}
private static void setDateAxis(XYPlot plot)
{
DateAxis domainAxis = new DateAxis();
domainAxis.setAutoTickUnitSelection(true);
domainAxis.setDateFormatOverride(new SimpleDateFormat("dd/MM"));
plot.setDomainAxis(domainAxis);
}
PS : base.setBackgroundPaint(Color.WHITE); doesn't work either
So it turns out that we have to set colors on different levels on jfreechart to control the colors of different parts.
In the image above the background color of the panel is white but the background color of the chart isn't. So, I had to use:
chart.setBackgroundPaint(Color.WHITE);
Im trying to override the getItemPaint() method of XYLineAndShapeRenderer by creating a subclass:
private class MultiRenderer extends XYLineAndShapeRenderer{
#Override
public Paint getItemPaint(int row, int column) {
if(row==2){
float x = getAmplitude()[column];
return floatToColor(x);
}
return super.getItemPaint(row, column);
}
private Color floatToColor(float val){...}
}
And calling:
XYPlot xyPlot = (XYPlot) myJFreeChartObject.getPlot();
MultiRenderer r = (MultiRenderer) xyPlot.getRenderer();
But im getting a ClassCastException. Is there a way I can cast this properly or override getItemPaint without creating a subclass? Im trying to plot 2 series as regular line plots and a 3rd series without a line and different color points. The first 2 series should have a line but no point markers.
Note getAmplitude() just returns an array of floats between 0 and 1
You can either use an Anonymous Inner Class
plot.setRenderer(new XYLineAndShapeRenderer() {
#Override
public Paint getItemPaint(int row, int column) {
if(row==2){
float x = getAmplitude()[column];
return floatToColor(x);
}
return super.getItemPaint(row, column);
}
private Color floatToColor(float val){...}
});
Or use a DrawingSupplier for your plot
I'm writing a game using Surfaceview and have a question relating to saving Data into a Bundle.
Initially, I had an arraylist which stored the Y co-ordinates (in the form of Integers) of sprites that will move only up and down. Declared as:
static ArrayList<Integer> ycoordinates = new ArrayList<Integer>();
I saved them to a Bundle using the following:
myBundle.putIntegerArrayList("myycoordinates", ycoordinates);
And restored them using this:
ycoordinates.addAll(savedState.getIntegerArrayList("ycoordinates"));
This all worked perfectly. However, I've had to change the whole coordinates system so it's based on Delta time to allow my sprites to move at a uniform speed across different screens. This is, again, working perfectly.
However, as a result of this change, I now have to store these values as floats rather than integers.
So, I am declaring as:
static ArrayList<Float> ycoordinates = new ArrayList<Float>();
So that's the background, now my question is, how do I store and restore values from a Float Arraylist? There doesn't seem to be a "putFloatArrayList" or "getFloatArrayList".
(I've used an Arraylist rather than an Array as the number of sprites needs to be dynamic).
Any help would be appreciated.
Many thanks
I've written a couple of simple methods to convert between List and float[]. You can use the Bundle putFloatArray() and getFloatArray on the float[].
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args){
List<Float> in = new ArrayList<Float>();
in.add(3.0f);
in.add(1f);
in.add((float)Math.PI);
List<Float>out = toList(toArray(in));
System.out.println(out);
}
public static float[] toArray(List<Float> in){
float[] result = new float[in.size()];
for(int i=0; i<result.length; i++){
result[i] = in.get(i);
}
return result;
}
public static List<Float> toList(float[] in){
List<Float> result = new ArrayList<Float>(in.length);
for(float f : in){
result.add(f);
}
return result;
}
}