如何在JavaSwing中制作交互式世界地图?
我想用 Java Swing 编写一个程序,它将显示世界地图,用户可以在其中单击每个国家/地区(即每个国家/地区都是一个按钮)。我认为最方便的方法是将世界地图图像拆分为按钮,但我不知道如何做到这一点。
我看了这个问题:Split image into clickable region,那里给出的答案显示了如何将图像拆分为矩形,但我必须将其拆分为任意形状。
这是我想要处理的图像:
回答
“一点点”数字运算可以定义图像中特定颜色的区域或形状。EG 从这个开始:
原始图像,裁剪并缩小为二值(2 色)图像,然后海洋洪水填满。除了在 Java 代码中完成的洪水填充之外的所有内容均未显示。
然后根据接近白色的颜色运行形状算法(请耐心等待 - 这需要一段时间),将产生一系列用于在顶部渲染的区域。添加了鼠标运动侦听器以将指针下方的区域着色为深绿色。
这是生成该图像的代码。鼠标指针当前指向中国。
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
/*
Outline code from:
/sf/ask/505281661/
*/
public class WorldMapArea {
private JComponent ui = null;
JLabel output = new JLabel();
public static final int SIZE = 750;
BufferedImage image;
Area area;
ArrayList<Shape> shapeList;
public WorldMapArea() {
try {
initUI();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void initUI() throws Exception {
if (ui != null) {
return;
}
URL url = new URL("https://i.stack.imgur.com/N4eOn.png");
image = ImageIO.read(url);
long then = System.currentTimeMillis();
System.out.println("" + then);
area = getOutline(Color.WHITE, image, 12);
long now = System.currentTimeMillis();
System.out.println("Time in mins: " + (now - then) / 60000d);
shapeList = separateShapeIntoRegions(area);
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
output.addMouseMotionListener(new MousePositionListener());
ui.add(output);
refresh();
}
public Area getOutline(Color target, BufferedImage bi, int tolerance) {
// construct the GeneralPath
GeneralPath gp = new GeneralPath();
boolean cont = false;
for (int xx = 0; xx < bi.getWidth(); xx++) {
for (int yy = 0; yy < bi.getHeight(); yy++) {
if (isIncluded(new Color(bi.getRGB(xx, yy)), target, tolerance)) {
//if (bi.getRGB(xx,yy)==targetRGB) {
if (cont) {
gp.lineTo(xx, yy);
gp.lineTo(xx, yy + 1);
gp.lineTo(xx + 1, yy + 1);
gp.lineTo(xx + 1, yy);
gp.lineTo(xx, yy);
} else {
gp.moveTo(xx, yy);
}
cont = true;
} else {
cont = false;
}
}
cont = false;
}
gp.closePath();
// construct the Area from the GP & return it
return new Area(gp);
}
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<>();
PathIterator pi = shape.getPathIterator(null);
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
class MousePositionListener implements MouseMotionListener {
@Override
public void mouseDragged(MouseEvent e) {
// do nothing
}
@Override
public void mouseMoved(MouseEvent e) {
refresh();
}
}
public static boolean isIncluded(Color target, Color pixel, int tolerance) {
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return ((rP - tolerance <= rT) && (rT <= rP + tolerance)
&& (gP - tolerance <= gT) && (gT <= gP + tolerance)
&& (bP - tolerance <= bT) && (bT <= bP + tolerance));
}
private void refresh() {
output.setIcon(new ImageIcon(getImage()));
}
private BufferedImage getImage() {
BufferedImage bi = new BufferedImage(
2 * SIZE, SIZE, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.drawImage(image, 0, 0, output);
g.setColor(Color.ORANGE.darker());
g.fill(area);
g.setColor(Color.RED);
g.draw(area);
try {
Point p = MouseInfo.getPointerInfo().getLocation();
Point p1 = output.getLocationOnScreen();
int x = p.x - p1.x;
int y = p.y - p1.y;
Point pointOnImage = new Point(x, y);
for (Shape shape : shapeList) {
if (shape.contains(pointOnImage)) {
g.setColor(Color.GREEN.darker());
g.fill(shape);
break;
}
}
} catch (Exception doNothing) {
}
g.dispose();
return bi;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
WorldMapArea o = new WorldMapArea();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.setResizable(false);
f.pack();
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
- (1+) worth the wait...
- ...............wow