How to Customize jzy3d Chart - java

I'm using org.jzy3d package (v 0.9) to create Surface plots.
Here's my code:
int stepsX = 6;
Range rX = new Range(1,6);
int stepsY = 7;
Range rY = new Range(0,6);
Mapper mapper = new Mapper(){
#Override
public double f(double x, double y) {
return //My function to get z;
}
};
org.jzy3d.plot3d.primitives.Shape surface = Builder.buildOrthonormal(new OrthonormalGrid(rX, stepsX, rY, stepsY), mapper);
surface.setColorMapper(new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax(), new org.jzy3d.colors.Color(1, 1, 1, .5f)));
surface.setFaceDisplayed(true);
surface.setWireframeDisplayed(false);
org.jzy3d.chart.Chart chart = new org.jzy3d.chart.Chart(Quality.Advanced,"swing");
chart.getScene().getGraph().add(surface);
IAxeLayout l = chart.getAxeLayout();
l.setXAxeLabel("Observation");
l.setYAxeLabel("Week");
l.setZAxeLabel("Rate");
l.setMainColor(org.jzy3d.colors.Color.GRAY);
JPanel p = new JPanel(new BorderLayout()); //another panel will be added to this panel and aligned left (BorderLayout.WEST)
p.add((JPanel)chart.getCanvas(),BorderLayout.CENTER);
... and this is what I get:
I'd like to customize this chart further, but I really cannot figure out how.
In particular I'd like to:
Zoom out the chart to fit my panel (in the attached picture you can see that the bottom of the chart is not visible);
Format axis labels (e.g. 0.6 displayed instead of 0.600000 for z axis, 2 displayed instead of 2.000 for x axis and so on...);
Invert color mapping (e.g. red when z value is lower, blue/green when z value is higher).

I solved by myself 2 of the 3 above mentioned points. Here are the solution just in case someone is interested:
Format Axis Labels: I createad a custom ITickRenderer which formats the axis labels basing on a DecimalFormat I have previously defined.
ITickRenderer r = new ITickRenderer(){
#Override
public String format(float arg0) {
return m.df.format(arg0);
}
};
Invert color mapping: I posted the solution here.

Related

Get the exact string position in PDF to be used later for changing it

Based on the answer for the question Get the exact Stringposition in PDF I can now get all the strings in a PDF file. Please have a look at the code:
PdfReader reader = new PdfReader("file.pdf");
RenderListener listener = new MyTextRenderListener();
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(1, listener);
static class MyTextRenderListener implements RenderListener {
#Override
public void renderText(TextRenderInfo renderInfo) {
String text = renderInfo.getText(); // line with text
}
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
#Override
public void renderImage(ImageRenderInfo renderInfo) { }
}
mkl in his answer wrote:
if yourRenderListenerin addition to inspecting the text
withgetText()also considersgetBaseline()or
evengetAscentLine()andgetDescentLine().you have all the
coordinates you will likely need.
In fact, TextRenderInfo has a few instances of LineSegment class which give some sort of coordinates. How do I use those coordinate (by transforming or extracting appropriate values from) to prepare a Rectangle object so the text that is found could be removed? A rectangle object has four coordinates that describe the position of the given text.
An example of removing strings (i.e. redacting) by using a Rectangle object can be found at SO (Remove text occurrences contained in a specified area with iText
)
UPDATE
I managed to do what I wanted by trial-and-error but I consider this a workaround and not a proper solution.
#Override
public void renderText(TextRenderInfo renderInfo) {
LineSegment baseline = renderInfo.getBaseline();
float x = baseline.getStartPoint().get(Vector.I1);
float y = baseline.getStartPoint().get(Vector.I2);
float xx = baseline.getEndPoint().get(Vector.I1);
float yy = baseline.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(x, yy, xx, y + 5);
}
Now I have a Rectangle object (note that I add 5 to one of its coordinates by playing with coordinate so that they cover all the string) and I can now redact the text. It works fine for unitary colours (e.g. white) when there is no image. When the text is on image or the page colour is in different colour than black, it will fail. That's why I describe my solution as a workaround. To me, it would be better to blank the text (replace it with empty string). How this could be done?
Response to mkl's comment
Not sure, if I've done it right:
LineSegment descentLine = renderInfo.getDescentLine();
float x = descentLine.getStartPoint().get(Vector.I1);
float y = descentLine.getStartPoint().get(Vector.I2);
float xx = descentLine.getEndPoint().get(Vector.I1);
float yy = descentLine.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(xx, yy, x, y);
I've used also the ascentLIne the same way. Unfortunetly, none of this have worked.
In all your attempts you tried to construct the rectangle from a single line, originally the base line, later the descent line. With such an approach you obviously don't have the height of the rectangle and can only guess.
Instead of that you should make use of both the descent and ascent lines!
E.g. assuming the simplified case of text drawn upright:
LineSegment ascentLine = renderInfo.getAscentLine();
LineSegment descentLine = renderInfo.getDescentLine();
float llx = descentLine.getStartPoint().get(Vector.I1);
float lly = descentLine.getStartPoint().get(Vector.I2);
float urx = ascentLine.getEndPoint().get(Vector.I1);
float ury = ascentLine.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(llx, lly, urx, ury);

How to define an offset for a PatternColor fill in iText?

I am trying to add tiled diagonal watermarks to the pdf, but it seems that pattern fills in iText are always tiled from the bottom left of the page, meaning that the tiles at the top and right side of the page can be cut abruptly. Is there an option to tile from the top left or with an offset instead?
Here is a sample of the code:
List<String> watermarkLines = getWatermarkLines();
Rectangle watermarkRect = getWatermarkRect();
PdfContentByte over = stamper.getOverContent(1);
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight();
for (int x = 0; x < watermarkLines.size(); x++) {
AffineTransform trans = getWatermarkTransform(watermarkLines, x);
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
}
over.setColorFill(new PatternColor(painter));
over.rectangle(0, 0, pageSize.getWidth(), pageSize.getHeight());
over.fill();
I tried changing the x and y of the rectangle function to negative or positive values, but it seems that the watermark is still stamped in the pattern as if it was tiled from the bottom left, cutting it in the same place as before.
First of, I cannot fathom which iText version you are using,
List<String> watermarkLines = getWatermarkLines();
...
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
implies that the third parameter of the ColumnText.showTextAligned method you use is typed as String or Object. The iText 5 version I have at hand, though, requires a Phrase there. Below I'll show how to apply an offset with the current iText 5.5.13. You'll have to check whether it also works for your version.
Yes, you can apply an offset... in the pattern definition!
If instead of
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight());
you create the pattern like this
PdfPatternPainter painter = over.createPattern(2 * watermarkRect.getWidth(), 2 * watermarkRect.getHeight(),
watermarkRect.getWidth(), watermarkRect.getHeight());
you have the same step size of pattern application (watermarkRect.getWidth(), watermarkRect.getHeight()) but a canvas twice that width and twice that height to position you text on. By positioning the text with an offset, you effectively move the whole pattern by that offset.
E.g. if you calculate the offsets as
Rectangle pageSize = pdfReader.getCropBox(1);
float xOff = pageSize.getLeft();
float yOff = pageSize.getBottom() + ((int)pageSize.getHeight()) % ((int)watermarkRect.getHeight());
and draw the text using
ColumnText.showTextAligned(painter, 0, new Phrase(watermarkLines.get(x)), (float) trans.getTranslateX() + xOff, (float) trans.getTranslateY() + yOff, 45f);
the pattern should fill the page as if starting at the top left corner of the visible page.
You haven't supplied getWatermarkLines, getWatermarkRect, and getWatermarkTransform. If I use
static AffineTransform getWatermarkTransform(List<String> watermarkLines, int x) {
return AffineTransform.getTranslateInstance(6 + 15*x, 6);
}
static Rectangle getWatermarkRect() {
return new Rectangle(65, 50);
}
static List<String> getWatermarkLines() {
return Arrays.asList("Test line 1", "Test line 2");
}
your original code for me creates a top left corner like this
and the code with the above offset creates one like this

mapping two points on image to another one

I am trying to map two points on one image to two points on the original image so i divided the work into three main actions first scaling the n rotation then translation after everything but cant position them correctly the scaling works fine and the translation also the rotation works perfectly if i didn't scale the images only way the rotation work perfectly when i rotate around custom point but the image get distorted
Rotate rotation = new Rotate();
rotation.setPivotX(proj.s2[0]);
rotation.setPivotY(proj.s2[1]);
MainView1.getTransforms().add(rotation);
MainView1.setManaged(false);
rotation.setAngle(Angle);
here is the code without custom rotation
guidebutton.setOnMouseClicked(event->{
if (!first_rot) {
proj.f2[0]=Lball.getCenterX();
proj.f2[1]= Lball.getCenterY();
proj.f1[0]=Rball.getCenterX();
proj.f1[1]= Rball.getCenterY();
MainView.setStyle("-fx-opacity : 0.0;");
guidetext.setText("now position them on the second image and click done");
first_rot=true;
}else {
proj.s2[0]=Lball.getCenterX();
proj.s2[1]= Lball.getCenterY();
proj.s1[0]=Rball.getCenterX();
proj.s1[1]= Rball.getCenterY();
//fixing the image first then fixing the points
// fixing the image
//adjusting the scale
double f[]=tranformations.dis_vec_d(proj.f1, proj.f2);//get the distance between the two points on the first image
double s[]=tranformations.dis_vec_d(proj.s1, proj.s2);//get the distance between the two points on the secondimage
double facx=f[0]/s[0];//factor of scale in x direction
double facy=f[1]/s[1];//factor of scale in y direction
//getting the position of second image inside the window
Bounds bounds = MainView1.getBoundsInLocal();
Bounds screenBounds = MainView1.localToScreen(bounds);
double x = screenBounds.getMinX();
double y = screenBounds.getMinY();
MainView1.setScaleX(facx);
// get the new position of image after scaling to adjust the position
bounds = MainView1.getBoundsInLocal();
screenBounds = MainView1.localToScreen(bounds);
double nx = screenBounds.getMinX();
double ny = screenBounds.getMinY();
double nmx = screenBounds.getMaxX()-nx;
double nmy = screenBounds.getMaxY()-ny;
MainView1.setTranslateX(x-nx);
MainView1.setTranslateY(y-ny);
double[]orig={nmx/2,nmy/2};
//adjusting rotation
//calculating the angle between the two line to adjust the rotation
double Angle=tranformations.angle_d(proj.s1, proj.s2);
Angle-=tranformations.angle_d(proj.f1, proj.f2);
//Add the Rotate to the ImageView's Transforms
MainView1.setRotate(Angle);
MainView1.setTranslateX(MainView.getTranslateX()+proj.f2[0]-proj.s2[0]);
MainView1.setTranslateY(MainView.getTranslateY()+proj.f2[1]-proj.s2[1]);
}
});
both views and points in unmanaged group "draw" when i get every thing work it get down when i use zooming when positioning points on the second image
i use this code for zooming using mouse wheel
final double SCALE_DELTA = 1.1;
draw.setOnScroll(event->{
event.consume();
if (event.getDeltaY() == 0) {
return;
}
double scaleFactor =(event.getDeltaY() > 0)? SCALE_DELTA: 1/SCALE_DELTA;
draw.setScaleX(draw.getScaleX() * scaleFactor);
draw.setScaleY(draw.getScaleY() * scaleFactor);
});
edit to explain the question more i have these two separate images and i use the two red points on lights as to correctly position them over each other to so they can form the new image complete image
First align one of the points using a translation then scale using the aligned point's coordinates as pivot and finally use the same pivot point to perform a rotation aligning the other points.
The following example uses groups containing 2 circles each, but it should be simple enough to replace centerX/centerY with the image coordinates of the points:
#Override
public void start(Stage primaryStage) throws Exception {
// create group containing scene that remains in place
Circle target1 = new Circle(100, 200, 20, Color.RED);
Circle target2 = new Circle(150, 100, 20, Color.RED);
Group targetGroup = new Group(target1, target2);
// create group that will be transformed
Circle c1 = new Circle(30, 30, 20, Color.BLUE);
Circle c2 = new Circle(400, 400, 20, Color.BLUE);
Group g = new Group(c1, c2);
Scene scene = new Scene(new Pane(targetGroup, g), 500, 500);
// register handler for swapping between transformed/untransformed scene on button click
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
boolean transformed;
final Translate translate = new Translate();
final Scale scale = new Scale();
final Rotate rotate = new Rotate();
{
// add transforms to transformation target
g.getTransforms().addAll(rotate, scale, translate);
}
#Override
public void handle(MouseEvent event) {
if (transformed) {
// reset transforms to identity
translate.setX(0);
translate.setY(0);
scale.setX(1);
scale.setY(1);
rotate.setAngle(0);
} else {
// align c1 and target1
translate.setX(target1.getCenterX() - c1.getCenterX());
translate.setY(target1.getCenterY() - c1.getCenterY());
// scale
double scaleFactor = Math.hypot(target1.getCenterX() - target2.getCenterX(), target1.getCenterY() - target2.getCenterY())
/ Math.hypot(c1.getCenterX() - c2.getCenterX(), c1.getCenterY() - c2.getCenterY());
scale.setPivotX(target1.getCenterX());
scale.setPivotY(target1.getCenterY());
scale.setX(scaleFactor);
scale.setY(scaleFactor);
// rotate
rotate.setPivotX(target1.getCenterX());
rotate.setPivotY(target1.getCenterY());
rotate.setAngle(Math.toDegrees(Math.atan2(target2.getCenterY() - target1.getCenterY(), target2.getCenterX() - target1.getCenterX())
- Math.atan2(c2.getCenterY() - c1.getCenterY(), c2.getCenterX() - c1.getCenterX())));
}
transformed = !transformed;
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
This is particularly simple using complex numbers.
An arbitrary similarity transform can be written
Z = a.z + b
where the modulus of a is the scaling factor, the argument of a is the rotation angle and b is the translation.
These coefficients are readily obtained from the known pairs of points by the usual two-points interpolation formula
Z = Z0 + (z - z0).(Z1 - Z0)/(z1 - z0)
or
a = (Z1 - Z0)/(z1 - z0)
b = Z0 - a.z0
You have the option of using a complex data type, or to retranscript the formulas in terms of real/imaginary parts.
If the square isn't rotated, then yeah, pick x and y independently.
If the square is rotated, the math gets a little trickier. Let's let the two end points of the diagonal be X and Y, represented as complex numbers.
Then look at the equation:
(Y - X)/(1 + i) x + X
When x = 0, this returns X. When x = 1 + i, it returns Y. In fact, this equation maps the unit rectangle onto the square whose diagonal's endpoints are X and Y.
So pick two random numbers 0 ≤ a, b ≤ 1, turn it into a random point a + bi on the unit rectangle, and then use the above equation to map into into a random point in the square.

MP Android Chart-How to fix the number of X-Axis values in the Line Chart?

private void plotchart(String s[], float[] f1) {
chart.setBackgroundColor(Color.rgb(63, 81, 181));
chart.setDescription("");
// enable touch gestures
chart.setTouchEnabled(true);
chart.setScaleEnabled(false);
chart.setPinchZoom(false);
chart.setGridBackgroundColor(Color.rgb(30, 46, 141));
chart.setDrawGridBackground(false);
chart.getAxisRight().setEnabled(false);
chart.setDrawMarkerViews(true);
chart.setDragEnabled(true);
chart.setViewPortOffsets(0f, 0f, 0f, 0f);
// chart.setVisibleXRangeMaximum(4);
chart.getLegend().setEnabled(false);
XAxis x = chart.getXAxis();
x.setEnabled(true);
x.setDrawGridLines(false);
x.setPosition(XAxis.XAxisPosition.BOTTOM_INSIDE);
x.setTextColor(Color.rgb(128, 128, 255));
x.isDrawLabelsEnabled();
x.setAxisLineColor(Color.BLACK);
x.setSpaceBetweenLabels(3);
x.setAvoidFirstLastClipping(true);
YAxis y = chart.getAxisLeft();
y.setAxisLineColor(Color.BLACK);
y.setTextColor(Color.BLACK);
y.setEnabled(true);
y.setAxisLineColor(Color.rgb(128, 128, 255));
y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
y.setDrawGridLines(false);
y.setLabelCount(5,true);
y.setGridColor(Color.rgb(128, 128, 255));
y.setDrawZeroLine(false);
y.setDrawLimitLinesBehindData(true);
y.setDrawGridLines(true);
y.setDrawLimitLinesBehindData(true);
y.setTextColor(Color.rgb(128, 128, 255));
// chart.setExtraOffsets(20f,2f,20f,2f);
ArrayList<com.github.mikephil.charting.data.Entry> entries= new ArrayList<>();
for (int i = 0; i < f1.length; i++) {
// Log.i("f1",f1[i]+"");
entries.add(new com.github.mikephil.charting.data.Entry(f1[i], i));
}
MymarkerView mv =new MymarkerView(this,R.layout.custom_marker_view);
chart.setMarkerView(mv);
LineDataSet dataset = new LineDataSet(entries, "");
dataset.isDrawCirclesEnabled();
dataset.setCircleRadius(0f);
dataset.setDrawFilled(true);
dataset.setFillColor(Color.rgb(0, 0, 0));
dataset.setLineWidth(0.2f);
dataset.setValueTextSize(0f);
dataset.setColor(Color.rgb(0, 0, 0));
// chart.setVisibleXRange(1, 5);
// chart.setVisibleYRangeMaximum(5, YAxis.AxisDependency.LEFT);
// chart.setClickable(true);
chart.invalidate();
ArrayList<String> time = new ArrayList<String>();
for (int i = 0; i < s.length; i++) {
time.add(i, s[i]);
}
LineData data = new LineData(time, dataset);
chart.setData(data);
}
I Have fixed the Y-Axis values by using setLabelCount() command.But how to fix the number of X-Axis values in my chart..For the above code i have the following chart formed.Chart1 Chart2..These are the two charts formed for different arguments given to the plotchart(String [],float[]) method.In all these charts I am facing the problem that it is not showing the fixed number of x-axis values.Somewhere it is showing 9 values and somewhere it is showing 7 values..And one more issue I am facing is that my first and the last Y-Axis values are getting hidden in my display.
setLabelCount() property is there for xAxis too.
XAxis xAxis = lineChart.getXAxis();
xAxis.setLabelCount(5);
If I understood your question well, it seems that you want to use the setVisibleXRangeMaximum method.
Restraining what's visible
setVisibleXRangeMaximum(float maxXRange): Sets the size of the area (range on the x-axis) that should be maximum visible at once. If this is e.g. set to 10, no more than 10 values on the x-axis can be viewed at once without scrolling.
This method is documented in the Viewport part of the MPAndroidChart tutorial, which is linked here.
Of course, I think you will have to use the setVisibleXRangeMinimum if you want to fix an upper and a lower bounds for the number of X values.
To set the X-axis values to a fixed number (10 for example):
float minXRange = 10;
float maxXRange = 10;
chart.setVisibleXRange(minXRange, maxXRange);
If the gap between your values on the X-axis is constant, it will also fix the scale for the X-axis.
To auto set the scale on the Y-Axis with 10% spacing according to your maximum and minimum values:
YAxis y1 = chart.getAxisLeft();
float percent = 10;
y1.setSpaceTop(percent);
y1.setSpaceBottom(percent);
You can use IndexAxisValueFormatter method of library
ArrayList<String> xAxisLabel = new ArrayList<>();
xAxisLabel.add("Mon");
xAxisLabel.add("Tue");
xAxisLabel.add("Wed");
xAxisLabel.add("Thu");
xAxisLabel.add("Fri");
xAxisLabel.add("Sat");
xAxisLabel.add("Sun");
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(texts));
Current library version v3.0.3
Set X-Axis values
xAxis.setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return (int) value + "";
}
});

GRAL - Axis styling - Keeping same Y axis across plots

I have a plot that I've created with GRAL:
GRAL plot
The problems I'm having are with the axis. Basically I'd like the Y axis to run from 0 .. 4 in every graph, and I can't find a way to force this behaviour. If I zoom out there is extra increments, but I'd like 0 .. 4 show up in the unzoomed plots.
I've also tried using setCustomTicks but the default ticks remain, is there a way to remove them and only use custom ones?
Help would be greatly appreciated!
The code for my plot is:
int[][] seq_data_200 = chunkArray(seq_data, 200);
DataTable[] listData = new DataTable[seq_data_200.length];
for (int i = 0; i < seq_data_200.length; i++){
DataTable data = new DataTable(Integer.class, Integer.class);
for (int j = 0; j < seq_data_200[i].length; j++) {
data.add(j, seq_data_200[i][j]);
}
listData[i] = data;
}
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
panel.setLayout(new GridLayout(seq_data_200.length,1,20,20));
for (int i = 0; i < listData.length; i++) {
XYPlot plot = new XYPlot(listData[i]);
LineRenderer lines = new DefaultLineRenderer2D();
plot.setLineRenderer(listData[i], lines);
Color color = new Color(0.0f, 0.3f, 1.0f);
plot.getPointRenderer(listData[i]).setColor(color);
plot.getLineRenderer(listData[i]).setColor(color);
double insetsTop = 20.0, insetsLeft = 60.0, insetsBottom = 60.0, insetsRight = 40.0;
plot.setInsets(new Insets2D.Double(insetsTop, insetsLeft, insetsBottom, insetsRight));
plot.getTitle().setText(getTitle(i, sequence));
plot.getAxisRenderer(XYPlot.AXIS_X).setLabel("Bases");
plot.getAxisRenderer(XYPlot.AXIS_X).setCustomTicks(getTicks(i, sequence));
plot.getAxisRenderer(XYPlot.AXIS_Y).setLabel("Number of " + nucleo + "'s");
plot.getAxisRenderer(XYPlot.AXIS_Y).setMinorTicksVisible(false);
plot.getAxisRenderer(XYPlot.AXIS_Y).setTickSpacing(1);
panel.add(new InteractivePanel(plot), BorderLayout.CENTER);
}
I found a somewhat hackish fix that works in the interim:
GRAL decides on axis height using the DataTable. It uses the max and min values of the table to define the viewport.
So if you add two pieces of Data:
DataTable.add(x,minY)
DataTable.add(x,maxY)
They'll be some spurious data on the graph, but the viewport will resize itself to your desired axis. Since these points are added at the end, they tend to be obscured somewhat, and are easy to ignore.
The following method worked for me:
plot.getAxis(XYPlot.AXIS_X).setRange(0.0, 4.0);
plot.getAxis(XYPlot.AXIS_Y).setRange(0.0, 20.0);

Categories