/* * Copyright (C) 2014 Alec Dhuse * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package co.foldingmap.map.visualization; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.text.NumberFormat; /** * * @author Alec */ public class LineGraph extends GraphPanel { protected BasicStroke thickLine, thinLine; protected GeneralPath line, fill; protected GraphData data; protected int xStart, yStart; protected int insetBottom, insetLeft, insetRight, insetTop; protected Line2D xAxis, yAxis; protected Line2D[] xTicks, yTicks; protected RenderingHints renderAntialiasing; protected String[] xLabels, yLabels; public LineGraph(GraphData data) { super(); setGraphData(data); insetBottom = 50; insetLeft = 50; insetRight = 50; insetTop = 50; renderAntialiasing = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); renderAntialiasing.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); thinLine = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); thickLine = new BasicStroke(8, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); } /* * Creates the Shapes used for the graph axes */ private void createAxis() { boolean useDecimal; float intMod, labelValX, labelValY; int numberOfLabelsX, numberOfLabelsY, tickX, tickY; numberOfLabelsY = ((getHeight() - 100) / 22) + 1; numberOfLabelsX = ((getWidth() - (insetLeft + insetRight)) / 40) + 1; xLabels = new String[numberOfLabelsX]; yLabels = new String[numberOfLabelsY]; xTicks = new Line2D[numberOfLabelsX]; yTicks = new Line2D[numberOfLabelsY]; float range = (data.getMaxX() - data.getMinX()); if (range >= 10) { float logVal = (float) Math.log10(range); float exponent = (logVal - 1); float interval = (float) Math.pow(10, exponent); int floor = (int) Math.floor(exponent); int mod = (int) Math.pow(10, floor); intMod = (int) Math.floor(interval / mod) * mod; useDecimal = false; } else { intMod = range / numberOfLabelsX; intMod = (float) (Math.floor(intMod * 100) * 0.01); useDecimal = true; } for (int i = 0; i < numberOfLabelsX; i++) { tickX = (xStart + (i * 40)); labelValX = ((intMod) * i) + data.getMinX(); xTicks[i] = new Line2D.Float(tickX, yStart - 4, tickX, yStart + 4); if (useDecimal) { NumberFormat formatter = new DecimalFormat("#0.0"); xLabels[i] = formatter.format(labelValX); } else { xLabels[i] = Integer.toString((int) labelValX); } } for (int i = 0; i < numberOfLabelsY; i++) { tickY = (yStart - (i * 22)); labelValY = (((data.getMaxY() - data.getMinY()) / numberOfLabelsY) * i) + data.getMinY(); yTicks[i] = new Line2D.Float((insetLeft - 8), tickY, insetLeft, tickY); //In case there is no data to show for Y-Axis, just increment the labels equal to i if ((data.getMaxY() - data.getMinY()) <= Float.MIN_VALUE) labelValY = i; yLabels[i] = Integer.toString((int) labelValY); } xAxis = new Line2D.Float(insetLeft, yStart, (getWidth() - insetRight), yStart); yAxis = new Line2D.Float(insetLeft, insetTop, insetLeft, yStart); } /* * Creates the Line graph and the fill under the graph. */ private void createGraph() { float adjustedValueX, valueRatioX, valueRatioY; float minX, maxX, maxY; float x, y; float yArea; line = new GeneralPath(); fill = new GeneralPath(); minX = data.getMinX(); maxX = data.getMaxX(); maxY = data.getMaxY(); yArea = yStart - insetTop; valueRatioY = yArea / (maxY * 1.1f); //move to origin fill.moveTo(xStart + 1, yStart - 1); for (int i = 0; i < data.size(); i++) { adjustedValueX = data.getX(i) - minX; valueRatioX = adjustedValueX / maxX; x = (valueRatioX * (getWidth() - 100)) + xStart + 1; y = yStart - valueRatioY * data.getY(i); if (i == 0) { line.moveTo(x, y); } else { line.lineTo(x, y); } fill.lineTo(x, y + 1); } //Move to x = 0, y = maxY fill.lineTo((getWidth() - insetRight), yStart); } @Override public void mouseClicked(MouseEvent me) { } @Override public void mousePressed(MouseEvent me) { } @Override public void mouseReleased(MouseEvent me) { } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { } @Override public void paint(Graphics g) { float labelPosX, labelPosY; FontMetrics fontMetrics; Graphics2D g2; int textHeight; //setup graphics g2 = (Graphics2D) g; g2.setRenderingHints(renderAntialiasing); //get text height fontMetrics = g2.getFontMetrics(font); textHeight = fontMetrics.getHeight(); xStart = insetLeft; yStart = getHeight() - insetBottom; createGraph(); createAxis(); g2.setColor(new Color(187, 203, 216)); //g2.setStroke(thickLine); g2.draw(yAxis); //g2.setStroke(thinLine); g2.draw(xAxis); //draw Y axis ticks g2.setColor(new Color(187, 203, 216)); //Draw x-axis ticks and labels for (int i = 0; i < xTicks.length; i++) { labelPosX = (float) (xTicks[i].getX1() - (fontMetrics.stringWidth(xLabels[i]) / 2)); labelPosY = (float) (xTicks[i].getY2() + 15); g2.setColor(new Color(187, 203, 216)); g2.draw(xTicks[i]); g2.setColor(Color.BLACK); g2.drawString(xLabels[i], labelPosX, labelPosY); } //Draw y-axis ticks and labels for (int i = 0; i < yTicks.length; i++) { labelPosX = (float) (yTicks[i].getX1() - fontMetrics.stringWidth(yLabels[i]) - 5); labelPosY = (float) (yTicks[i].getY1() + (textHeight / 2f) - 2); g2.setColor(new Color(187, 203, 216)); g2.draw(yTicks[i]); g2.setColor(Color.BLACK); g2.drawString(yLabels[i], labelPosX, labelPosY); } //draw the top line g2.setColor(new Color(56, 98, 24)); g2.draw(line); //fill line down to x-axis g2.setColor(new Color(207, 232, 202)); g2.fill(fill); //Draw X Axis Label Rectangle2D xAxisLabelBounds = fontMetrics.getStringBounds(data.getXAxisLabel(), g); float insideWidth = this.getWidth() - insetLeft; float xAxisLabelX = (float) ((insideWidth / 2.0) - (xAxisLabelBounds.getWidth() / 2.0)); labelPosY = (float) xTicks[0].getY2() + textHeight + 20; g2.setColor(Color.BLACK); g2.drawString(data.getXAxisLabel(), xAxisLabelX, labelPosY); } public final void setGraphData(GraphData data) { this.data = data; } }