Swing中绘制超级拥挤的点

Drawing super crowded points in Swing

我需要能够通过 Swing 为图形绘制非常拥挤的点。例如:让我们看看以下几点:

p1=(35.19589389346247,32.10152879327731),

p2 = (35.20319591121872,32.10318254621849),

p3 = (35.20752617756255,32.1025646605042),

p4 = (35.21007339305892,32.10107446554622),

p5 = (35.21310882485876,32.104636394957986),

等...

我想画它们,但是可以看到,它们的坐标非常密集。另外,扩大比例也没有用,只是移动了框内的点。

下面是我的尝试:

private void Oval(Graphics2D g2, String id, int locationX, int locationY) {
        AffineTransform old = g2.getTransform();
        g2.scale(4,4);
        g2.setPaint(Color.blue);
        g2.fillOval(locationX - Radius, locationY - Radius, Radius * 2, Radius * 2);
        g2.setPaint(Color.black);
        g2.drawString(id, locationX + Radius, locationY - Radius);
        g2.setTransform(old);
    }

考虑到面板尺寸为(1000,1000),这是点绘制的代码。

这是点绘图的输出:

如您所见,它们相互覆盖,这显然不是我想要做的。我的目标是将它们分开,以便我们可以清楚地看到这些点。

因此,基本概念是对“虚拟”坐标(您的点)进行某种“转换”到(屏幕的)“物理”坐标

您确实尝试通过缩放图形上下文来做到这一点,但这样做的问题是它也会缩放球的大小,这并不是您真正想要做的。

以下是一种可能解决方案的基本示例。

它计算由点表示的区域的 min/max 范围,然后使用组件大小平移点,使它们适合可见 space

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GraphPoint {
        private String id;
        private Point2D point;

        public GraphPoint(String id, Point2D point) {
            this.id = id;
            this.point = point;
        }

        public String getId() {
            return id;
        }

        public Point2D getPoint() {
            return point;
        }

    }

    public class TestPane extends JPanel {

        private List<GraphPoint> points;
        private int radius = 10;

        private double virtualScale = 1.0;

        private Point2D minRange;
        private Point2D maxRange;

        public TestPane() {
            points = new ArrayList<>(16);
            points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
            points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
            points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
            points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
            points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));

            double minX = Double.MAX_VALUE;
            double maxX = Double.MIN_VALUE;
            double minY = Double.MAX_VALUE;
            double maxY = Double.MIN_VALUE;

            for (GraphPoint gp : points) {
                minX = Math.min(minX, gp.getPoint().getX());
                maxX = Math.max(maxX, gp.getPoint().getX());
                minY = Math.min(minY, gp.getPoint().getY());
                maxY = Math.max(maxY, gp.getPoint().getY());
            }

            minRange = new Point2D.Double(minX, minY);
            maxRange = new Point2D.Double(maxX, maxY);

            double xRange = maxRange.getX() - minRange.getX();
            double yRange = maxRange.getY() - minRange.getY();

            System.out.println(minRange.getX() + " - " + minRange.getY());
            System.out.println(maxRange.getX() + " - " + maxRange.getY());
            System.out.println(xRange + " - " + yRange);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (GraphPoint gp : points) {
                paintPoint(g2d, gp);
            }
            g2d.dispose();
        }

        private void paintPoint(Graphics2D g2d, GraphPoint gp) {
            Graphics2D g2 = (Graphics2D) g2d.create();

            Point2D translated = translate(gp);

            double xPos = translated.getX();
            double yPos = translated.getY();

            double offset = radius;

            g2.translate(xPos - offset, yPos - offset);
            g2.setPaint(Color.blue);
            g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));
            g2.dispose();
        }

        protected Point2D translate(GraphPoint gp) {

            double xRange = maxRange.getX() - minRange.getX();
            double yRange = maxRange.getY() - minRange.getY();

            double offset = radius;
            double width = getWidth() - (offset * 2);
            double height = getHeight() - (offset * 2);

            double xScale = width / xRange;
            double yScale = height / yRange;

            Point2D original = gp.getPoint();

            double x = offset + ((original.getX() - minRange.getX()) * xScale);
            double y = offset + ((original.getY() - minRange.getY()) * yScale);

            System.out.println(gp.getId() + " " + x + " x " + y);

            return new Point2D.Double(x, y);
        }
    }
}

我必须强调,这是一种可能的解决方案,您的实际要求可能会有所不同,但这应该为您提供一个起点,您可以从中定义自己的算法,例如,您可以定义自己的 min/max 范围(即 34x30 到 36x33)

the String I have tried now for 1 hour and I didn't get it I did edit your code already you help us a lot. the string above the points the id I mean point "0" this string above the point if u can help us or show us it will be appreciated a lot!

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GraphPoint {

        private String id;
        private Point2D point;

        public GraphPoint(String id, Point2D point) {
            this.id = id;
            this.point = point;
        }

        public String getId() {
            return id;
        }

        public Point2D getPoint() {
            return point;
        }

    }

    public class TestPane extends JPanel {

        private List<GraphPoint> points;
        private int radius = 10;

        private double virtualScale = 1.0;

        private Point2D minRange;
        private Point2D maxRange;

        public TestPane() {
            points = new ArrayList<>(16);
            points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
            points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
            points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
            points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
            points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));

            double minX = Double.MAX_VALUE;
            double maxX = Double.MIN_VALUE;
            double minY = Double.MAX_VALUE;
            double maxY = Double.MIN_VALUE;

            for (GraphPoint gp : points) {
                minX = Math.min(minX, gp.getPoint().getX());
                maxX = Math.max(maxX, gp.getPoint().getX());
                minY = Math.min(minY, gp.getPoint().getY());
                maxY = Math.max(maxY, gp.getPoint().getY());
            }

            minRange = new Point2D.Double(minX, minY);
            maxRange = new Point2D.Double(maxX, maxY);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();

            FontMetrics fm = g2d.getFontMetrics();
            double insets = fm.getHeight() + radius;

            // I'm lazy, so I'm drawing the lines first, then painting
            // the points over the top as I can't be bothered to workout
            // a clever way to ensure the lines are always painted under
            // the dots
            GraphPoint lastPoint = null;
            for (GraphPoint gp : points) {
                if (lastPoint != null) {
                    paintLine(g2d, lastPoint, gp, insets);
                }
                lastPoint = gp;
            }
            for (GraphPoint gp : points) {
                paintPoint(g2d, gp, insets);
            }
            g2d.dispose();
        }

        private void paintLine(Graphics2D g2d, GraphPoint from, GraphPoint to, double insets) {
            Point2D fromPoint = translate(from, insets);
            Point2D toPoint = translate(to, insets);
            g2d.setColor(Color.RED);
            g2d.draw(new Line2D.Double(fromPoint, toPoint));
        }

        private void paintPoint(Graphics2D g2d, GraphPoint gp, double insets) {
            Graphics2D g2 = (Graphics2D) g2d.create();

            Point2D translated = translate(gp, insets);

            double xPos = translated.getX();
            double yPos = translated.getY();

            double offset = radius;

            g2.translate(xPos - offset, yPos - offset);
            g2.setPaint(Color.blue);
            g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));

            FontMetrics fm = g2d.getFontMetrics();
            String text = gp.getId();
            double x = xPos - (fm.stringWidth(text) / 2);
            double y = (yPos - radius - fm.getHeight()) + fm.getAscent();
            g2d.drawString(text, (float)x, (float)y);

            g2.dispose();
        }

        protected Point2D translate(GraphPoint gp, double insets) {
            double xRange = maxRange.getX() - minRange.getX();
            double yRange = maxRange.getY() - minRange.getY();

            double offset = insets;
            double width = getWidth() - (offset * 2);
            double height = getHeight() - (offset * 2);

            double xScale = width / xRange;
            double yScale = height / yRange;

            Point2D original = gp.getPoint();

            double x = offset + ((original.getX() - minRange.getX()) * xScale);
            double y = offset + ((original.getY() - minRange.getY()) * yScale);

            System.out.println(gp.getId() + " " + x + " x " + y);

            return new Point2D.Double(x, y);
        }
    }
}

文本的技巧是理解我已经翻译了上下文的原点,所以 0x0 位置实际上是圆的中间,所以你需要从中减去文本偏移量。

我还进行了轻微更新以允许工作流传入一个 insets 值,这将减少可用的可见区域以“帮助”补偿文本

another question can make a line with an arrow in the tip

见...

  • Java make a directed line and make it move

一些想法。请注意,这变得越来越复杂,因为箭头需要旋转到他们正在看的点, 之类的东西可能会有所帮助