Question

I am creating a Java program that receives and plots GPS coordinates over a TCP socket connection on a 2D image of a map. The constructor creates the JFrame and graphical components and then starts off a SwingWorker thread to handle getting coordinates from the socket and then drawing an oval to represent the fix on the map.

I am able to always receive data over the connection but the program does not reliably draw points over the image.

Any suggestions and thank you!

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

import connectivity.TelitSocketServer;
import calculations.PlottingMath;
import objects.ElephantFix;

@SuppressWarnings("serial")
public class Gui 
{
    protected JFrame mainWindow;

    protected JPanel mapArea = new JPanel();

    private ReceiveFix rfix;

    public Gui()
    {
        // TODO Auto-generated constructor stub

        mainWindow =  new JFrame();

        // Load map image
        Image map = null;
        try
        {
            File mapImage = new File("map_images/AWE_PLOT.JPG");
            map = ImageIO.read(mapImage);

        } catch (Exception e)
        {
            System.out.println(e.toString());
        }



        JLabel label = new JLabel(new ImageIcon(map));
        mapArea.add(label);


        // Map Image Dimensions

        mainWindow.getContentPane().add(mapArea, "Center");

        mainWindow.setSize(471, 670);
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainWindow.setTitle("ElePlotter");
        mainWindow.setVisible(true);

        rfix = new ReceiveFix();
        rfix.execute();

}

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

}

private class ReceiveFix extends SwingWorker<Void, ElephantFix>
{

    @Override
    protected Void doInBackground()
    {
        // Start the server
        String fix = "";

        TelitSocketServer currentConnection = new TelitSocketServer();
        try
        {
            // Wait for client to connect
            currentConnection.intializeConnection();

            while (true)
            {
                // Parse and convert received GPS fix into arc radians  
                fix = currentConnection.readLine();

                String[] split = fix.split(" ");


                double latWholeDegrees = Double.parseDouble(split[0]
                        .substring(0, 3));
                double longWholeDegrees = Double.parseDouble(split[1]
                        .substring(0, 3));

                double latMinutes = Double.parseDouble(split[0]
                        .substring(3)) * .166667;
                double longMinutes = Double.parseDouble(split[1]
                        .substring(3)) * .166667;


                double lat = latWholeDegrees - latMinutes / 10;
                double lon = longWholeDegrees + longMinutes / 10;

                publish(new ElephantFix(lat, lon));
            }

        } catch (Exception e)
        {

            e.printStackTrace();
        }

        // Return null if somehow unable to publish node data
        return null;

    }


    @Override
    protected void process(List<ElephantFix> fixes)
    {
        int x, y;
        // Get the most recently published node
        ElephantFix aFix = fixes.get(fixes.size() - 1);



        // Translate lat/long into map X/Y pixel
        x = PlottingMath.getCurrentPixelX(aFix.getLatitude(),
                aFix.getLongitude());
        y = PlottingMath.getCurrentPixelY(aFix.getLatitude(),
                aFix.getLongitude());


        // Plot on image
        Graphics g = mapArea.getGraphics();

        g.setColor(Color.RED);

        g.fillOval(x, y, 15, 15);

        // mapArea.validate();
        //
        // mapArea.repaint();
        //
        // Gui.this.repaint();

    }
}

}
Was it helpful?

Solution 2

You should not be drawing with a Graphics object obtained by calling getGraphics() on a Component. Doing this will give you a Graphics object that doesn't persist and an image that doesn't persist. Instead either draw on a BufferedImage with its Graphics object and display it in the GUI, or iterate through the data collection in the JPanel's paintComponent method and draw with the data obtained.

OTHER TIPS

mapArea.getGraphics(); is not how custom painting in Swing is done.

Start by taking a look at Performing Custom Painting for more details.

Adding a component to a container ensures that the child component will always be painted over the container...

JLabel label = new JLabel(new ImageIcon(map));
mapArea.add(label);

You have that backwards. It might be better to add both the label and mapArea to the same container

The next problem you will need to overcome is the fact that you will need to get your painting component to reside over the top of your label.

To this end you could...

Update with example

There are any number of ways to achieve this, you could use multiple components which act as layers onto which you could add other components or perform custom painting.

This is not as easy it as it sounds. Most layout managers don't consider two components sharing the same space. You also have to manage the connection between the layers, so if the map is centered within the lowest layer, but the available space is larger or smaller than the map, offsets begin to drift...messy...

A simpler solution would be to use a single component and render all the content onto it, for example...

Map

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestMap {

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

    public TestMap() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                MapPane pane = new MapPane();
                pane.dropPinAt(174, 147);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(pane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MapPane extends JPanel {

        private BufferedImage img;
        private BufferedImage pin;
        private List<Point> pins;

        public MapPane() {
            pins = new ArrayList<>(25);
            try {
                img = ImageIO.read(getClass().getResource("/Map.jpg"));
                pin = ImageIO.read(getClass().getResource("/Pin.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (img != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                int x = (getWidth() - img.getWidth()) / 2;
                int y = (getHeight() - img.getHeight()) / 2;

                g2d.drawImage(img, x, y, this);

                for (Point p : pins) {
                    // Offset the pin x/y
                    // to allow the point to land on the desired location
                    p.x -= 6;
                    p.y -= 64;
                    System.out.println(p);
                    g2d.drawImage(pin, p.x, p.y, this);

                }

                g2d.dispose();
            }
        }

        protected void dropPinAt(int x, int y) {
            pins.add(new Point(x, y));
        }
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top