Java2D曲线图

直接贴上代码,有时间在补充详细说明

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import javax.imageio.ImageIO;

public class AxisImage {

    private static int MARGIN = 10;
    private static int AXIS_TEXT_INTERVAL = 3;
    private static Font FONT_TITLE = new Font("Dialog", Font.BOLD, 20);
    private static Font FONT_DEFAULT = new Font("Dialog", Font.PLAIN, 12);
    private static Color COLOR_TITLE = Color.BLACK;
    private static Color COLOR_AXIS = Color.LIGHT_GRAY;
    private static Color COLOR_LINE = Color.BLACK;
    private static Color COLOR_POINT = Color.BLUE;
    private static int AXIS_HEIGHT = 8;
    private static DecimalFormat format = new DecimalFormat("0.##");
    private int width = 831;
    private int height = 544;
    //--------------
    private int leftBorderWidth, rightBorderWidth, topBorderHeight, bottomBorderHeight;
    private int yAxisLength, xAxisLength;
    private double xMin, xMax, xInterval;
    private double yMin, yMax, yInterval;
    private int xSecant = 8, ySecant = 8;
    //--------------
    private String imageTitle = "Dynamic Curve";
    private String xTitle = "Production Year", yTitle = "Production Rate";
    private String filePath;
    private ArrayList<Double> dataX, dataY;

    public AxisImage(String filePath, ArrayList<Double> dataX, ArrayList<Double> dataY) {
        this.filePath = filePath;
//        this.dataX = dataX;
//        this.dataY = dataY;
        initData();
    }

    public static boolean isEmpty(String str) {
        if (str == null || str.equals("")) {
            return true;
        }
        return false;
    }

    private void initData2() {
        dataX = new ArrayList<Double>();
        dataX.add(2009d);
        dataX.add(2010d);
        dataX.add(2011d);
        dataX.add(2012d);
        dataX.add(2013d);
        dataX.add(2014d);
        dataX.add(2015d);
        dataX.add(2016d);

        dataY = new ArrayList<Double>();
        dataY.add(2009d);
        dataY.add(2010d);
        dataY.add(2011d);
        dataY.add(2012d);
        dataY.add(2013d);
        dataY.add(2014d);
        dataY.add(2015d);
        dataY.add(2016d);
    }

    private void initData() {
        dataX = new ArrayList<Double>();
        dataX.add(1970d);
        dataX.add(1971d);
        dataX.add(1972d);
        dataX.add(1973d);
        dataX.add(1974d);
        dataX.add(1975d);
        dataX.add(1976d);
        dataX.add(1977d);
        dataX.add(1978d);
        dataX.add(1979d);
        dataX.add(1980d);
        dataX.add(1981d);
        dataX.add(1982d);
        dataX.add(1983d);
        dataX.add(1984d);
        dataX.add(1985d);
        dataX.add(1986d);
        dataX.add(1987d);
        dataX.add(1988d);
        dataX.add(1989d);
        dataX.add(1990d);
        dataX.add(1991d);
        dataX.add(1992d);
        dataX.add(1993d);
        dataX.add(1994d);
        dataX.add(1995d);
        dataX.add(1996d);
        dataX.add(1997d);
        dataX.add(1998d);
        dataX.add(1999d);
        dataX.add(2000d);
        dataX.add(2001d);
        dataX.add(2002d);
        dataX.add(2003d);

        dataY = new ArrayList<Double>();
        dataY.add(2792.15);
        dataY.add(5278.07);
        dataY.add(5865.31);
        dataY.add(6646.68);
        dataY.add(5616.4);
        dataY.add(4714.56);
        dataY.add(4053.61);
        dataY.add(3981.79);
        dataY.add(3984.92);
        dataY.add(3815.58);
        dataY.add(3819.5);
        dataY.add(3423.39);
        dataY.add(3174.36);
        dataY.add(3417.84);
        dataY.add(3067.86);
        dataY.add(2743.75);
        dataY.add(2383.52);
        dataY.add(2456.8);
        dataY.add(2295.45);
        dataY.add(2284.7);
        dataY.add(3069.38);
        dataY.add(2596.44);
        dataY.add(2293.16);
        dataY.add(2145.34);
        dataY.add(2177.07);
        dataY.add(2094.92);
        dataY.add(1746.51);
        dataY.add(2126.63);
        dataY.add(2796.62);
        dataY.add(2954.27);
        dataY.add(3026.86);
        dataY.add(3667.41);
        dataY.add(4937.66);
        dataY.add(6000.18);
    }

    public String generate() {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) image.getGraphics();
        paintImage(g);
        return saveFile(filePath, image);
    }

    private void paintImage(Graphics2D g) {
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //填充背景
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.BLACK);


        calculateMaxMin();
        //计算并校正坐标轴的起始值及其分隔
        adjustMaxMin();

        //计算坐标轴及边界范围
        calculateAxisLength();

        drawImageTitle(g);
        drawXTitle(g);
        drawYTitle(g);
        drawXAxis(g);
        drawYAxis(g);

        drawPoints(g);
    }

    private void drawImageTitle(Graphics2D g0) {
        if (isEmpty(imageTitle)) {
            return;
        }
        Graphics2D g = (Graphics2D) g0.create();
        g.setColor(COLOR_TITLE);
        g.setFont(FONT_TITLE);

        TextLayout layout = new TextLayout(imageTitle, g.getFont(), g.getFontRenderContext());
        int x = (int) ((width - layout.getBounds().getWidth()) / 2);
        int y = MARGIN + (int) layout.getAscent();
        g.drawString(imageTitle, x, y);
        g.dispose();
    }

    private void drawXTitle(Graphics2D g0) {
        if (isEmpty(xTitle)) {
            return;
        }
        Graphics2D gx = (Graphics2D) g0.create();
        gx.setColor(COLOR_TITLE);
        gx.setFont(FONT_TITLE);

        TextLayout layout = new TextLayout(xTitle, gx.getFont(), gx.getFontRenderContext());
        int x = (int) (leftBorderWidth + (xAxisLength - layout.getBounds().getWidth()) / 2);
        int y = topBorderHeight + yAxisLength + bottomBorderHeight - MARGIN - (int) layout.getDescent();
        gx.drawString(xTitle, x, y);
        gx.dispose();
    }

    private void drawYTitle(Graphics2D g0) {
        if (yTitle == null || yTitle.trim().equals("")) {
            return;
        }

        Graphics2D gy = (Graphics2D) g0.create();
        gy.setFont(FONT_TITLE);
        gy.setColor(COLOR_TITLE);


        AffineTransform at = new AffineTransform();
        at.setToRotation((Math.PI) / 2.0 * 3.0);
        gy.transform(at);

        TextLayout layout = new TextLayout(yTitle, gy.getFont(), gy.getFontRenderContext());
        int x = (int) -(topBorderHeight + (yAxisLength + layout.getBounds().getWidth()) / 2);
        int y = MARGIN + (int) layout.getAscent();
        gy.drawString(yTitle, x, y);
        gy.dispose();
    }

    private void drawXAxis(Graphics2D g0) {
        Graphics2D gx = (Graphics2D) g0.create();

        //计算每个坐标轴分割线之间的间距
        int xInterLen = xAxisLength / xSecant;

        for (int i = 0; i < xSecant + 1; i++) {
            //画X轴上面一个一个小的分割线
            gx.setColor(COLOR_AXIS);
            gx.drawLine(leftBorderWidth + i * xInterLen,
                    topBorderHeight + yAxisLength,
                    leftBorderWidth + i * xInterLen,
                    topBorderHeight + yAxisLength + AXIS_HEIGHT);

            //画一个一个小的分割线所表示的值
            gx.setColor(COLOR_TITLE);
            String axisText = format.format(xMin + i * xInterval);
            TextLayout layout = new TextLayout(axisText, gx.getFont(), new FontRenderContext(null, false, false));
            gx.drawString(axisText,
                    leftBorderWidth + i * xInterLen - (int) layout.getBounds().getWidth() / 2,
                    topBorderHeight + yAxisLength + AXIS_HEIGHT + layout.getAscent() + AXIS_TEXT_INTERVAL);
        }

        //画X轴方向上下两条轴线
        gx.setColor(COLOR_LINE);
        gx.drawLine(leftBorderWidth, topBorderHeight, leftBorderWidth + xAxisLength, topBorderHeight);
        gx.drawLine(leftBorderWidth, topBorderHeight + yAxisLength, leftBorderWidth + xAxisLength, topBorderHeight + yAxisLength);
        gx.dispose();
    }

    private void drawYAxis(Graphics2D g0) {
        Graphics2D gy = (Graphics2D) g0.create();
        //计算每个坐标轴分割线之间的间距
        int yInterLen = yAxisLength / ySecant;

        for (int i = 0; i < ySecant + 1; i++) {
            //画Y轴上面一个一个小的分割线
            gy.setColor(COLOR_AXIS);
            gy.drawLine(leftBorderWidth - AXIS_HEIGHT,
                    (int) (topBorderHeight + yAxisLength - i * yInterLen),
                    leftBorderWidth,
                    (int) (topBorderHeight + yAxisLength - i * yInterLen));

            //画Y轴上面坐标轴中间的水平的分割线
            if (i != 0 && i != ySecant) {
                gy.drawLine(leftBorderWidth,
                        (int) (topBorderHeight + yAxisLength - i * yInterLen),
                        leftBorderWidth + xAxisLength,
                        (int) (topBorderHeight + yAxisLength - i * yInterLen));
            }

            //画一个一个小的分割线所表示的值
            gy.setColor(COLOR_TITLE);
            gy.setFont(FONT_DEFAULT);
            String axisText = format.format(yMax - i * yInterval);
            TextLayout layout = new TextLayout(axisText, gy.getFont(), new FontRenderContext(null, false, false));
            gy.drawString(axisText,
                    leftBorderWidth - AXIS_HEIGHT - AXIS_TEXT_INTERVAL - (int) layout.getBounds().getWidth(),
                    topBorderHeight + i * yInterLen + (int) layout.getDescent());
        }

        //画Y轴方向左右两条轴线
        gy.setColor(COLOR_LINE);
        gy.drawLine(leftBorderWidth, topBorderHeight, leftBorderWidth, topBorderHeight + yAxisLength);
        gy.drawLine(leftBorderWidth + xAxisLength, topBorderHeight,
                leftBorderWidth + xAxisLength, topBorderHeight + yAxisLength);
        gy.dispose();
    }

    private void drawPoints(Graphics2D g0) {
        Graphics2D g = (Graphics2D) g0.create();
        double xScope = xMax - xMin;
        double yScope = yMax - yMin;

        int prevX = -1;
        int prevY = -1;
        g.setColor(COLOR_POINT);
        for (int i = 0; i < dataX.size(); i++) {
            Double xValue = dataX.get(i);
            Double yValue = dataY.get(i);

            if (xValue > xMax || xValue < xMin
                    || yValue > yMax || yValue < yMin) {
                continue;
            }

            int x = leftBorderWidth + (int) ((xValue - xMin) / xScope * xAxisLength);
            int y = topBorderHeight + yAxisLength - (int) ((yValue - yMin) / yScope * yAxisLength);
            g.fillOval(x - 5, y - 5, 10, 10);

            if (i != 0) {
                g.drawLine(x, y, prevX, prevY);
            }
            prevX = x;
            prevY = y;
        }
    }

    private String saveFile(String filePath, BufferedImage image) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(filePath));
            ImageIO.write(image, "jpg", fos);
            fos.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
            return "";
        } finally {
            try {
                fos.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return filePath;
    }

    private void calculateMaxMin() {
        int length = dataX.size();
        if (length != 0) {
            xMin = dataX.get(0);
            xMax = dataX.get(0);
            yMin = dataY.get(0);
            yMax = dataY.get(0);

            for (int i = 0; i < length; i++) {
                double xValue = dataX.get(i);
                xMin = Math.min(xMin, xValue);
                xMax = Math.max(xMax, xValue);

                double yValue = dataY.get(i);
                yMin = Math.min(yMin, yValue);
                yMax = Math.max(yMax, yValue);
            }
        }
    }

    private void adjustMaxMin() {
//        System.out.println("xMax = " + xMax + ", xMin = " + xMin + ", xInterval = " + xInterval + ", xSecant = " + xSecant);
        AxisImage.AxisAdjust adjustX = new AxisImage.AxisAdjust(xMax, xMin, xSecant);
        xMax = adjustX.getMax();
        xMin = adjustX.getMin();
        xInterval = adjustX.getInterval();
        xSecant = adjustX.getSecant();
//        System.out.println("xMax = " + xMax + ", xMin = " + xMin + ", xInterval = " + xInterval + ", xSecant = " + xSecant);

//        System.out.println("yMax = " + yMax + ", yMin = " + yMin + ", yInterval = " + yInterval + ", ySecant = " + ySecant);
        AxisImage.AxisAdjust adjustY = new AxisImage.AxisAdjust(yMax, yMin, ySecant);
        yMax = adjustY.getMax();
        yMin = adjustY.getMin();
        yInterval = adjustY.getInterval();
        ySecant = adjustY.getSecant();
//        System.out.println("yMax = " + yMax + ", yMin = " + yMin + ", yInterval = " + yInterval + ", ySecant = " + ySecant);
    }

    private void calculateAxisLength() {
        //初步计算边界里坐标轴的距离
        //四周空白+坐标轴标题高度,坐标轴标题和图片标题高度一致
        TextLayout layout1 = new TextLayout(xTitle, FONT_TITLE, new FontRenderContext(null, false, false));
        int borderWidth1 = (int) Math.ceil(layout1.getAscent() + layout1.getDescent()) + MARGIN * 2;
        leftBorderWidth = borderWidth1;
        rightBorderWidth = borderWidth1;
        topBorderHeight = borderWidth1;
        bottomBorderHeight = borderWidth1;

        //下方再加上坐标轴上的各小分割线上值的高度
        TextLayout layout2 = new TextLayout("1980", FONT_DEFAULT, new FontRenderContext(null, false, false));
        int borderWidth2 = (int) Math.ceil(layout2.getAscent() + layout2.getDescent()) + AXIS_TEXT_INTERVAL * 2;
        bottomBorderHeight += borderWidth2;


        //左方要加上各个数字的最大宽度
        //找到最宽的数字,然后计算其高度
        String maxStr = format.format(yMin);
        for (int i = 1; i < ySecant + 1; i++) {
            String str = format.format(yMin + i * yInterval);
            if (str.length() > maxStr.length()) {
                maxStr = str;
            }
        }
        TextLayout layout3 = new TextLayout(maxStr, FONT_DEFAULT, new FontRenderContext(null, false, false));
        int borderWidth3 = (int) Math.ceil(layout3.getBounds().getWidth()) + AXIS_TEXT_INTERVAL * 2;
        leftBorderWidth += borderWidth3;


        //初步计算坐标轴的长度
        yAxisLength = height - topBorderHeight - bottomBorderHeight;
        xAxisLength = width - leftBorderWidth - rightBorderWidth;

        //对坐标轴和边界数字进行校正
        int lessX = xAxisLength % xSecant;
        xAxisLength -= lessX;
        bottomBorderHeight += lessX;

        int lessY = yAxisLength % ySecant;
        yAxisLength -= lessY;
        rightBorderWidth += lessY;
    }

    public static void main(String[] args) {
        AxisImage axisImage = new AxisImage("F:/1.jpg", new ArrayList<Double>(), new ArrayList<Double>());
        System.out.println(axisImage.generate());
    }

    private class AxisAdjust {

        private double max;
        private double min;
        private int secant;
        private double interval;

        public AxisAdjust(double max, double min, int groupNo) {
            this.max = max;
            this.min = min;
            this.secant = groupNo;
            adjust();
        }

        public double getInterval() {
            return interval;
        }

        public double getMax() {
            return max;
        }

        public double getMin() {
            return min;
        }

        public int getSecant() {
            return secant;
        }

        private void adjust2() {
            //1 计算间隔大小
            interval = (max - min) / secant;
//        System.out.println("interval = " + interval);

            //2 看看是10的多少次方(取整),同时计算出10的当前次方的一半作为标准值用于校正
            double m10 = Math.floor(Math.log10(interval));
            double standardValue = 5 * Math.pow(10, m10 - 1);
//        System.out.println("m10 = " + m10 + ", standardValue = " + standardValue);

            //3 间隔值取出最高位剩下的余数
            double yInterval1 = interval % Math.pow(10, Math.floor(m10));
//        System.out.println("interval1 = " + yInterval1);

            //4 对interval的值进行校正
            //比如m10=2,那么standardValue = 50
            //对于余数:
            //(a)当大于50时,在50-100的区间内舍入(75以下舍到50,75及其以上入到100)
            //(b)当小于50是,在0-50的区间内舍入(25以下舍到0,25及其以上入到50)
            //(c)同时,如果是舍去,secant间隔数加1
            double d;
            if (yInterval1 > standardValue) {
//            System.out.println(">");
                d = Math.round((yInterval1 - standardValue) * 2 / Math.pow(10, m10));
                if (d == 0) {
                    secant += 1;
                    interval = (Math.floor(interval / Math.pow(10, Math.floor(m10))) + 0.5) * Math.pow(10, m10);
                } else {
                    interval = (Math.floor(interval / Math.pow(10, Math.floor(m10))) + 1) * Math.pow(10, m10);
                }
            } else {
//            System.out.println("<");
                d = Math.round(yInterval1 * 2 / Math.pow(10, m10));
                if (d == 0) {
                    secant += 1;
                    interval = Math.floor(interval / Math.pow(10, m10)) * Math.pow(10, m10);
                } else {
                    interval = (Math.floor(interval / Math.pow(10, m10)) + 0.5) * Math.pow(10, m10);
                }
            }
//        System.out.println("d = " + d);
            System.out.println("interval = " + interval);

            //5 根据新计算的interval重新校正min,max的值
//            min = Math.floor(min / interval) * interval;
//            max = Math.ceil(max / interval) * interval;
            max = min + interval * secant;
            System.out.println("max = " + max + ", min = " + min);
        }

        private void adjust() {
            //1 初步计算大概区分多少个区间,如果区间大于10,向上取一半
            double scope = max - min;
            secant = (int) (scope / (5 * Math.pow(10, Math.floor(Math.log10(scope)) - 1)));
//            System.out.println("secant = " + secant);
            if (secant > 10) {
                secant = (secant + 1) / 2;
            }

            //2 计算间隔大小
            interval = (max - min) / secant;
//            System.out.println("interval = " + interval);

            //3 看看是10的多少次方(取整),同时计算出10的当前次方的一半作为标准值用于校正
            double m10 = Math.floor(Math.log10(interval));
            double standardValue = 5 * Math.pow(10, m10 - 1);
//            System.out.println("m10 = " + m10 + ", standardValue = " + standardValue);

            //4 间隔值取出最高位剩下的余数
            double yInterval1 = 0;
            if (m10 > 0) {
                yInterval1 = interval % Math.pow(10, Math.floor(m10));
            } else {
                double temp = Math.pow(10, -m10);
                yInterval1 = (interval * temp) % (Math.pow(10, Math.floor(m10)) * temp) / temp;
            }
//            System.out.println("interval1 = " + yInterval1);

            //5 对interval的值进行校正
            //比如m10=2,那么standardValue = 50
            //对于余数:
            //(a)当大于50时,在50-100的区间内舍入(75以下舍到50,75及其以上入到100)
            //(b)当小于50是,在0-50的区间内舍入(25以下舍到0,25及其以上入到50)
            //(c)同时,如果是舍去,secant间隔数加1
            double d = 0;
            if (yInterval1 == 0 || yInterval1 == standardValue) {
            } else if (yInterval1 > standardValue) {
//                System.out.println(">");
                d = Math.round((yInterval1 - standardValue) * 2 / Math.pow(10, m10));
                if (d == 0) {
                    secant++;
                    interval = (Math.floor(interval / Math.pow(10, Math.floor(m10))) + 0.5) * Math.pow(10, m10);
                } else {
                    interval = (Math.floor(interval / Math.pow(10, Math.floor(m10))) + 1) * Math.pow(10, m10);
                }
            } else {
//                System.out.println("<");
                d = Math.round(yInterval1 * 2 / Math.pow(10, m10));
                if (d == 0) {
                    secant++;
                    interval = Math.floor(interval / Math.pow(10, m10)) * Math.pow(10, m10);
                } else {
                    interval = (Math.floor(interval / Math.pow(10, m10)) + 0.5) * Math.pow(10, m10);
                }
            }
//            System.out.println("d = " + d);
//            System.out.println("interval = " + interval);

            //6 根据新计算的interval重新校正min、max的值
            //  同时需要验证所划分间隔总数是否能将原始数据全部包含,否则再添加一个间隔区间
            min = Math.floor(min / interval) * interval;
            max = Math.ceil(max / interval) * interval;
            if (interval * secant < scope) {
                secant++;
            }
//            System.out.println("max = " + max + ", min = " + min);
        }
    }
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值