Announcement

Collapse
No announcement yet.

Problem with lat / long accuracy in PositionEvent vs MouseEvent

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Problem with lat / long accuracy in PositionEvent vs MouseEvent

    Good afternoon

    I am still fairly new to WorldWind. I have tried to see if the issue that I am having is possible to do and could not find the exact problem (although many were close) that I am seeing (tried searching here and Google). Basically I need to be able to take a MouseEvent and get the latitude and longitude from it. I need to be able to take that value and convert it into other coordinate systems so the user can paste it into a different screen. What I have tried is to take the MouseEvent's x,y and use the call in the View called computePositionFromScreenPoint(x,y) to get a position. I then can get the latitude and longitude from that. What I noticed is that it is not the same latitude and longitude as the one in my status bar that uses a PositionListener and gets the Position from the PositionEvent. They are close in value but when converting them to another coordinate system the numbers come out very different. What I am trying to figure out is if there might be something simple that I am missing or if I should try and tie into the PositionEvent instead of the MouseEvent.

    Thank you for any help that you can provide
    Mike

    Here is an example of what I am seeing. I took the FlatWorld demo and updated the StatusBar to include a MouseMotionListener. The Lat and Lon values are the ones that originally came with the demo. The LatX and LonY values are the ones that are set from the compute call using the MouseEvent.

    Click image for larger version

Name:	coordinateErrorTest.jpg
Views:	1
Size:	125.9 KB
ID:	111966

    Here is the StatusBar class that I added the MouseMotionListener to

    Code:
    /*
     * Copyright (C) 2012 United States Government as represented by the Administrator of the
     * National Aeronautics and Space Administration.
     * All Rights Reserved.
     */
    package gov.nasa.worldwind.util;
    
    import gov.nasa.worldwind.*;
    import gov.nasa.worldwind.event.*;
    import gov.nasa.worldwind.geom.*;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.beans.*;
    import java.net.URL;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    /**
     * @author tag
     * @version $Id: StatusBar.java 1171 2013-02-11 21:45:02Z dcollins $
     */
    public class StatusBar extends JPanel implements PositionListener, MouseMotionListener, RenderingListener
    {
        // Units constants TODO: Replace with UnitsFormat
        public final static String UNIT_METRIC = "gov.nasa.worldwind.StatusBar.Metric";
        public final static String UNIT_IMPERIAL = "gov.nasa.worldwind.StatusBar.Imperial";
    
        protected static final int MAX_ALPHA = 254;
    
        private WorldWindow eventSource;
        private String elevationUnit = UNIT_METRIC;
        private String angleFormat = Angle.ANGLE_FORMAT_DD;
    
        protected final JLabel latDisplay = new JLabel("");
        protected final JLabel lonDisplay = new JLabel(Logging.getMessage("term.OffGlobe"));
        protected final JLabel latXDisplay = new JLabel("");
        protected final JLabel lonYDisplay = new JLabel(Logging.getMessage("term.OffGlobe"));
        protected final JLabel altDisplay = new JLabel("");
        protected final JLabel eleDisplay = new JLabel("");
    
        protected AtomicBoolean showNetworkStatus = new AtomicBoolean(true);
        protected AtomicBoolean isNetworkAvailable = new AtomicBoolean(true);
        protected Thread netCheckThread;
    
        public StatusBar()
        {
            super(new GridLayout(1, 0));
    
            final JLabel heartBeat = new JLabel(Logging.getMessage("term.Downloading"));
    
            altDisplay.setHorizontalAlignment(SwingConstants.CENTER);
            latDisplay.setHorizontalAlignment(SwingConstants.CENTER);
            lonDisplay.setHorizontalAlignment(SwingConstants.CENTER);
            latXDisplay.setHorizontalAlignment(SwingConstants.CENTER);
            lonYDisplay.setHorizontalAlignment(SwingConstants.CENTER);
            eleDisplay.setHorizontalAlignment(SwingConstants.CENTER);
    
            this.add(altDisplay);
            this.add(latDisplay);
            this.add(latXDisplay);
            this.add(lonDisplay);
            this.add(lonYDisplay);
            this.add(eleDisplay);
            this.add(heartBeat);
    
            heartBeat.setHorizontalAlignment(SwingConstants.CENTER);
            heartBeat.setForeground(new java.awt.Color(255, 0, 0, 0));
    
            Timer downloadTimer = new Timer(100, new ActionListener()
            {
                public void actionPerformed(java.awt.event.ActionEvent actionEvent)
                {
                    if (!showNetworkStatus.get())
                    {
                        if (heartBeat.getText().length() > 0)
                            heartBeat.setText("");
                        return;
                    }
    
                    if (!isNetworkAvailable.get())
                    {
                        heartBeat.setText(Logging.getMessage("term.NoNetwork"));
                        heartBeat.setForeground(new Color(255, 0, 0, MAX_ALPHA));
                        return;
                    }
    
                    Color color = heartBeat.getForeground();
                    int alpha = color.getAlpha();
                    if (isNetworkAvailable.get() && WorldWind.getRetrievalService().hasActiveTasks())
                    {
                        heartBeat.setText(Logging.getMessage("term.Downloading"));
                        if (alpha >= MAX_ALPHA)
                            alpha = MAX_ALPHA;
                        else
                            alpha = alpha < 16 ? 16 : Math.min(MAX_ALPHA, alpha + 20);
                    }
                    else
                    {
                        alpha = Math.max(0, alpha - 20);
                    }
                    heartBeat.setForeground(new Color(255, 0, 0, alpha));
                }
            });
            downloadTimer.start();
    
            this.netCheckThread = this.startNetCheckThread();
    
            WorldWind.getNetworkStatus().addPropertyChangeListener(NetworkStatus.HOST_UNAVAILABLE,
                new PropertyChangeListener()
                {
                    public void propertyChange(PropertyChangeEvent evt)
                    {
                        Object nv = evt.getNewValue();
                        String message = Logging.getMessage("NetworkStatus.UnavailableHost",
                            nv != null && nv instanceof URL ? ((URL) nv).getHost() : "Unknown");
                        Logging.logger().info(message);
                    }
                });
    
            WorldWind.getNetworkStatus().addPropertyChangeListener(NetworkStatus.HOST_AVAILABLE,
                new PropertyChangeListener()
                {
                    public void propertyChange(PropertyChangeEvent evt)
                    {
                        Object nv = evt.getNewValue();
                        String message = Logging.getMessage("NetworkStatus.HostNowAvailable",
                            nv != null && nv instanceof URL ? ((URL) nv).getHost() : "Unknown");
                        Logging.logger().info(message);
                    }
                });
        }
    
        protected NetworkCheckThread startNetCheckThread()
        {
            NetworkCheckThread nct = new NetworkCheckThread(this.showNetworkStatus, this.isNetworkAvailable, null);
            nct.setDaemon(true);
            nct.start();
    
            return nct;
        }
    
        public void setEventSource(WorldWindow newEventSource)
        {
            if (this.eventSource != null)
            {
                this.eventSource.removePositionListener(this);
                this.eventSource.removeRenderingListener(this);
                this.eventSource.getInputHandler().removeMouseMotionListener(this);
            }
    
            if (newEventSource != null)
            {
                newEventSource.addPositionListener(this);
                newEventSource.addRenderingListener(this);
                newEventSource.getInputHandler().addMouseMotionListener(this);
            }
    
            this.eventSource = newEventSource;
        }
    
        public boolean isShowNetworkStatus()
        {
            return showNetworkStatus.get();
        }
    
        public void setShowNetworkStatus(boolean showNetworkStatus)
        {
            this.showNetworkStatus.set(showNetworkStatus);
    
            if (showNetworkStatus)
            {
                if (this.netCheckThread != null)
                    this.netCheckThread.interrupt();
    
                this.netCheckThread = this.startNetCheckThread();
            }
            else
            {
                if (this.netCheckThread != null)
                    this.netCheckThread.interrupt();
    
                this.netCheckThread = null;
            }
        }
    
        public void moved(PositionEvent event)
        {
            this.handleCursorPositionChange(event);
        }
    
        public WorldWindow getEventSource()
        {
            return this.eventSource;
        }
    
        public String getElevationUnit()
        {
            return this.elevationUnit;
        }
    
        public void setElevationUnit(String unit)
        {
            if (unit == null)
            {
                String message = Logging.getMessage("nullValue.StringIsNull");
                Logging.logger().severe(message);
                throw new IllegalArgumentException(message);
            }
    
            this.elevationUnit = unit;
        }
    
        public String getAngleFormat()
        {
            return this.angleFormat;
        }
    
        public void setAngleFormat(String format)
        {
            if (format == null)
            {
                String message = Logging.getMessage("nullValue.StringIsNull");
                Logging.logger().severe(message);
                throw new IllegalArgumentException(message);
            }
    
            this.angleFormat = format;
        }
    
        protected String makeCursorElevationDescription(double metersElevation)
        {
            String s;
            String elev = Logging.getMessage("term.Elev");
            if (UNIT_IMPERIAL.equals(elevationUnit))
                s = String.format(elev + " %,7d feet", (int) (WWMath.convertMetersToFeet(metersElevation)));
            else // Default to metric units.
                s = String.format(elev + " %,7d meters", (int) metersElevation);
            return s;
        }
    
        protected String makeEyeAltitudeDescription(double metersAltitude)
        {
            String s;
            String altitude = Logging.getMessage("term.Altitude");
            if (UNIT_IMPERIAL.equals(elevationUnit))
                s = String.format(altitude + " %,7d mi", (int) Math.round(WWMath.convertMetersToMiles(metersAltitude)));
            else // Default to metric units.
                s = String.format(altitude + " %,7d km", (int) Math.round(metersAltitude / 1e3));
            return s;
        }
    
        protected String makeAngleDescription(String label, Angle angle)
        {
            String s;
            if (Angle.ANGLE_FORMAT_DMS.equals(angleFormat))
                s = String.format("%s %s", label, angle.toDMSString());
            else
                s = String.format("%s %7.4f\u00B0", label, angle.degrees);
            return s;
        }
    
        protected void handleCursorPositionChange(PositionEvent event)
        {
            Position newPos = event.getPosition();
            if (newPos != null)
            {
                String las = makeAngleDescription("Lat", newPos.getLatitude());
                String los = makeAngleDescription("Lon", newPos.getLongitude());
                String els = makeCursorElevationDescription(
                    eventSource.getModel().getGlobe().getElevation(newPos.getLatitude(), newPos.getLongitude()));
                latDisplay.setText(las);
                lonDisplay.setText(los);
                eleDisplay.setText(els);
            }
            else
            {
                latDisplay.setText("");
                lonDisplay.setText(Logging.getMessage("term.OffGlobe"));
                eleDisplay.setText("");
            }
        }
    
        public void stageChanged(RenderingEvent event)
        {
            if (!event.getStage().equals(RenderingEvent.BEFORE_BUFFER_SWAP))
                return;
    
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    if (eventSource.getView() != null && eventSource.getView().getEyePosition() != null)
                        altDisplay.setText(makeEyeAltitudeDescription(
                            eventSource.getView().getEyePosition().getElevation()));
                    else
                        altDisplay.setText(Logging.getMessage("term.Altitude"));
                }
            });
        }
    
    	public void mouseDragged(MouseEvent e)
    	{
    	}
    
    	public void mouseMoved(MouseEvent e)
    	{
    		handleCursorPositionChange(e);
    	}
    	
        protected void handleCursorPositionChange(MouseEvent e)
        {
            Position newPos = ((WorldWindow)e.getSource()).getView().computePositionFromScreenPoint(e.getX(), e.getY());
            if (newPos != null)
            {
                String las = makeAngleDescription("LatX", newPos.getLatitude());
                String los = makeAngleDescription("LonY", newPos.getLongitude());
                latXDisplay.setText(las);
                lonYDisplay.setText(los);
            }
        }
    }

  • frenchy
    replied
    Hi,
    Comparing degrees is quite complex, because it simply depends on where is the position you want to check : at the poles, a small angular difference means a small distance, at the equator it is a large one... You can consider two coordinates are the same perhaps at the 7 or 8th decimal place... Yours are very different.
    If you project down to meters, in a square world, you can then compare things, and you'll have probably hundreds of meters of difference...
    I've been involved in projections for a while, distortions are common, especially at the poles. The datum/ellipsoid value is very important, along with the projection algorithm of course. In WWJ, there are some mistakes at the poles (extra blurryness), and also with the Political borders, that don't fit well at many places... That's a problem.

    Leave a comment:


  • marnott
    replied
    Good morning and thank you for the quick reply.

    I had actually worried that my conversions were the issue which is why I used one of the examples in the WorldWind toolkit to test it out. I ended up using the FlatWorld demo because I have a Flat Earth currently (hoping to allow 2D to 3D switching later) but the demo is great in that it allowed me to see if there was just an issue in the Flat World since it allows you to switch from 2D to 3D. This is when I noticed that the values in both 2D and 3D did not match each other when calculating using the MouseEvent coordinates (using computePositionFromScreenPoint(x,y)) vs getting the lat and long from the PositionEvent. As I said they were very close in that I would get the following values (as shown in the example image)
    MouseEvent Latitude: 31.1426
    PositionEvent Latitude: 31.1404
    MouseEvent Longitude: -92.2861
    PositionEvent Longitude: -92.2962

    The discrepency between the two seems to get closer or farther apart depending on your zoom level and appeared when selecting either the Round World or the Flat World in the WorldWind example. In the end I need to be able to convert the Lat and Long to MGRS which seems to get thrown off by the different 3rd decimal place.

    I wasn't sure if it is something that I was simply missing but I may try and somehow hook the PositionEvent into my flow instead of the MouseEvent so that I am looking at the exact same values

    Thank you again for your reply
    Mike

    As a side note I just wanted to thank everyone who made this product and who help on these forums. This is an amazing toolkit and the examples and forum posts help immensely with being able to do some pretty cool things. I feel that I have only scratched the surface and I look forward to working with it more.

    Leave a comment:


  • nlneilson
    replied
    The StatusBar code should be accurate to as close as your mouse position BUT I have not tried it in the FlatWorld view.
    WW is and was meant as a 3D app. If you want/need to use FlatWorld you may need to make some changes yourself.
    There may be some parallax error in the flat mode, just guessing on that.

    "They are close in value but when converting them to another coordinate system the numbers come out very different."
    That is a pretty good indication your 'converting' code may be the cause om much of your problem.

    I modified the StatusBar code so I have one on top and one on the bottom. The bottom is fairly similar to the SDK code except for what is displayed and the format. The top has the center position of the screen to display the LatLon and for picking it displays additional data. I have not had any problems with accuracy.

    First I suggest checking your 'converting' code and see how close your data is in the 3D view.

    Leave a comment:

Working...
X