views:

268

answers:

2

I have a wide variety of locations stored in my persistent object that contain latitudes and longitudes in double(43.7389, 7.42577) format. I need to be able to grab the user's latitude and longitude and select all items within, say 1 mile. Walking distance. I have done this in PHP so I snagged my PHP code and transferred it to Java, where everything plugged in fine until I figured out J2ME doesn't support atan2(double, double). So, after some searching, I find a small snippet of code that is supposed to be a substitute for atan2. Here is the code:

public double atan2(double y, double x) {
        double coeff_1 = Math.PI / 4d;
        double coeff_2 = 3d * coeff_1;
        double abs_y = Math.abs(y)+ 1e-10f;
        double r, angle;
        if (x >= 0d) {
            r = (x - abs_y) / (x + abs_y);
            angle = coeff_1;
        } else {
            r = (x + abs_y) / (abs_y - x);
            angle = coeff_2;
        }

        angle += (0.1963f * r * r - 0.9817f) * r;

        return y < 0.0f ? -angle : angle;
    }

I am getting odd results from this. My min and max latitude and longitudes are coming back as incredibly low numbers that can't possibly be right. Like 0.003785746 when I am expecting something closer to the original lat and long values (43.7389, 7.42577).

Since I am no master of advanced math, I don't really know what to look for here. Perhaps someone else may have an answer.

Here is my complete code:

package store_finder;

import java.util.Vector;

import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;

import net.rim.blackberry.api.invoke.Invoke;
import net.rim.blackberry.api.invoke.MapsArguments;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;

public class nearBy extends MainScreen {
    private HorizontalFieldManager _top;
    private VerticalFieldManager _middle;
    private int horizontalOffset;
    private final static long animationTime = 300;
    private long animationStart = 0;
    private double latitude = 43.7389;
    private double longitude = 7.42577;
    private int _interval = -1;

    private double max_lat;
    private double min_lat;
    private double max_lon;
    private double min_lon;
    private double latitude_in_degrees;
    private double longitude_in_degrees;


    public nearBy()
    {
        super();

        horizontalOffset = Display.getWidth();

        _top = new HorizontalFieldManager(Manager.USE_ALL_WIDTH | Field.FIELD_HCENTER)
        {
            public void paint(Graphics gr)
            {
                Bitmap bg = Bitmap.getBitmapResource("bg.png");
                gr.drawBitmap(0, 0, Display.getWidth(), Display.getHeight(), bg, 0, 0);
                subpaint(gr);
            }           
        };
        _middle = new VerticalFieldManager()
        {
            public void paint(Graphics graphics)
            {
                graphics.setBackgroundColor(0xFFFFFF);
                graphics.setColor(Color.BLACK);
                graphics.clear();
                super.paint(graphics);

            }
            protected void sublayout(int maxWidth, int maxHeight)
            {
                int displayWidth = Display.getWidth();
                int displayHeight = Display.getHeight();

                super.sublayout( displayWidth, displayHeight);
                setExtent( displayWidth, displayHeight);
            }  
        };

        add(_top);
        add(_middle);

        Bitmap lol = Bitmap.getBitmapResource("logo.png");
        BitmapField lolfield = new BitmapField(lol);
        _top.add(lolfield);

        Criteria cr= new Criteria();
        cr.setCostAllowed(true);
        cr.setPreferredResponseTime(60);
        cr.setHorizontalAccuracy(5000);
        cr.setVerticalAccuracy(5000);
        cr.setAltitudeRequired(true);
        cr.isSpeedAndCourseRequired();
        cr.isAddressInfoRequired();

        try{    
            LocationProvider lp = LocationProvider.getInstance(cr);
            if( lp!=null ){
                lp.setLocationListener(new LocationListenerImpl(), _interval, 1, 1); 
            }           
        }
        catch(LocationException le)
        {
            add(new RichTextField("Location exception "+le));
        }

        //_middle.add(new RichTextField("this is a map " + Double.toString(latitude) + " " + Double.toString(longitude)));
        int lat = (int) (latitude * 100000);
        int lon = (int) (longitude * 100000);
        String document = "<location-document>" + "<location lon='" + lon + "' lat='" + lat + "' label='You are here' description='You' zoom='0' />" + "<location lon='742733' lat='4373930' label='Hotel de Paris' description='Hotel de Paris' address='Palace du Casino' postalCode='98000' phone='37798063000' zoom='0' />" + "</location-document>";
       // Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments( MapsArguments.ARG_LOCATION_DOCUMENT, document));

        _middle.add(new SeparatorField());
        surroundingVenues();
        _middle.add(new RichTextField("max lat: " + max_lat));
        _middle.add(new RichTextField("min lat: " + min_lat));
        _middle.add(new RichTextField("max lon: " + max_lon));
        _middle.add(new RichTextField("min lon: " + min_lon));
    }

    private void surroundingVenues()
    {
        double point_1_latitude_in_degrees = latitude;
        double point_1_longitude_in_degrees= longitude;

        // diagonal distance +  error margin
        double distance_in_miles = (5 * 1.90359441) + 10;

        getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, 45);
        double lat_limit_1 = latitude_in_degrees;
        double lon_limit_1 = longitude_in_degrees;

        getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, 135);
        double lat_limit_2 = latitude_in_degrees;
        double lon_limit_2 = longitude_in_degrees;

        getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, -135);
        double lat_limit_3 = latitude_in_degrees;
        double lon_limit_3 = longitude_in_degrees;

        getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, -45);
        double lat_limit_4 = latitude_in_degrees;
        double lon_limit_4 = longitude_in_degrees;

        double mx1 = Math.max(lat_limit_1, lat_limit_2);
        double mx2 = Math.max(lat_limit_3, lat_limit_4);
        max_lat = Math.max(mx1, mx2);

        double mm1 = Math.min(lat_limit_1, lat_limit_2);
        double mm2 = Math.min(lat_limit_3, lat_limit_4);
        min_lat = Math.max(mm1, mm2);

        double mlon1 = Math.max(lon_limit_1, lon_limit_2);
        double mlon2 = Math.max(lon_limit_3, lon_limit_4);
        max_lon = Math.max(mlon1, mlon2);

        double minl1 = Math.min(lon_limit_1, lon_limit_2);
        double minl2 = Math.min(lon_limit_3, lon_limit_4);
        min_lon = Math.max(minl1, minl2);

        //$qry = "SELECT DISTINCT zip.zipcode, zip.latitude, zip.longitude, sg_stores.* FROM zip JOIN store_finder AS sg_stores ON sg_stores.zip=zip.zipcode WHERE zip.latitude<=$lat_limit_max AND zip.latitude>=$lat_limit_min AND zip.longitude<=$lon_limit_max AND zip.longitude>=$lon_limit_min";
    }

    private void getCords(double point_1_latitude, double point_1_longitude, double distance, int degs)
    {
        double m_EquatorialRadiusInMeters = 6366564.86; 
        double m_Flattening=0;

        double distance_in_meters   = distance * 1609.344 ;

        double direction_in_radians = Math.toRadians( degs );

        double eps = 0.000000000000005;

        double r = 1.0 - m_Flattening;

        double point_1_latitude_in_radians  = Math.toRadians( point_1_latitude   );
        double point_1_longitude_in_radians = Math.toRadians( point_1_longitude );

        double tangent_u = (r * Math.sin( point_1_latitude_in_radians ) ) / Math.cos( point_1_latitude_in_radians );

        double sine_of_direction = Math.sin( direction_in_radians );

        double cosine_of_direction = Math.cos( direction_in_radians );

        double heading_from_point_2_to_point_1_in_radians = 0.0;

        if ( cosine_of_direction != 0.0 )
        {
            heading_from_point_2_to_point_1_in_radians = atan2( tangent_u, cosine_of_direction ) * 2.0;
        }

             double cu = 1.0 / Math.sqrt( ( tangent_u * tangent_u ) + 1.0 );
             double su = tangent_u * cu;
             double sa = cu * sine_of_direction;
             double c2a = ( (-sa) * sa ) + 1.0;
            double x= Math.sqrt( ( ( ( 1.0 /r /r ) - 1.0 ) * c2a ) + 1.0 ) + 1.0;
           x= (x- 2.0 ) / x;
            double c= 1.0 - x;
           c= ( ( (x * x) / 4.0 ) + 1.0 ) / c;
            double d= ( ( 0.375 * (x * x) ) -1.0 ) * x;

         tangent_u = distance_in_meters /r / m_EquatorialRadiusInMeters /c;

            double y= tangent_u;

            boolean exit_loop = false;

            double cosine_of_y = 0.0;
            double cz          = 0.0;
            double e          = 0.0;
            double term_1      = 0.0;
            double term_2      = 0.0;
            double term_3      = 0.0;
            double sine_of_y   = 0.0;

            while( exit_loop != true )
            {
               sine_of_y = Math.sin(y);
              cosine_of_y = Math.cos(y);
              cz = Math.cos( heading_from_point_2_to_point_1_in_radians + y);
              e = (cz * cz * 2.0 ) - 1.0;
              c = y;
              x = e * cosine_of_y;
              y = (e + e) - 1.0;

              term_1 = ( sine_of_y * sine_of_y * 4.0 ) - 3.0;
              term_2 = ( ( term_1 * y * cz * d) / 6.0 ) + x;
              term_3 = ( ( term_2 * d) / 4.0 ) -cz;
              y= ( term_3 * sine_of_y * d) + tangent_u;

               if ( Math.abs(y - c) > eps )
               {
                  exit_loop = false;
               }
               else
               {
                  exit_loop = true;
               }
            }

            heading_from_point_2_to_point_1_in_radians = ( cu * cosine_of_y * cosine_of_direction ) - ( su * sine_of_y );

           c = r * Math.sqrt( ( sa * sa ) + ( heading_from_point_2_to_point_1_in_radians * heading_from_point_2_to_point_1_in_radians ) );
           d = ( su * cosine_of_y ) + ( cu * sine_of_y * cosine_of_direction );

           double point_2_latitude_in_radians = atan2(d, c);

           c = ( cu * cosine_of_y ) - ( su * sine_of_y * cosine_of_direction );
           x = atan2( sine_of_y * sine_of_direction, c);
           c = ( ( ( ( ( -3.0 * c2a ) + 4.0 ) * m_Flattening ) + 4.0 ) * c2a * m_Flattening ) / 16.0;
           d = ( ( ( (e * cosine_of_y * c) + cz ) * sine_of_y * c) + y) * sa;

           double point_2_longitude_in_radians = ( point_1_longitude_in_radians + x) - ( ( 1.0 - c) * d * m_Flattening );

           heading_from_point_2_to_point_1_in_radians = atan2( sa, heading_from_point_2_to_point_1_in_radians ) + Math.PI;

           latitude_in_degrees    = Math.toRadians( point_2_latitude_in_radians  );
           longitude_in_degrees = Math.toRadians( point_2_longitude_in_radians );

    }

    public double atan2(double y, double x) {
        double coeff_1 = Math.PI / 4d;
        double coeff_2 = 3d * coeff_1;
        double abs_y = Math.abs(y)+ 1e-10f;
        double r, angle;
        if (x >= 0d) {
            r = (x - abs_y) / (x + abs_y);
            angle = coeff_1;
        } else {
            r = (x + abs_y) / (abs_y - x);
            angle = coeff_2;
        }

        angle += (0.1963f * r * r - 0.9817f) * r;

        return y < 0.0f ? -angle : angle;
    }



    private Vector fetchVenues(double max_lat, double min_lat, double max_lon, double min_lon)
    {
        return new Vector();
    }

    private class LocationListenerImpl implements LocationListener { 
        public void locationUpdated(LocationProvider provider, Location location) {
            if(location.isValid()) {
                nearBy.this.longitude = location.getQualifiedCoordinates().getLongitude();
                nearBy.this.latitude = location.getQualifiedCoordinates().getLatitude();

                //double altitude = location.getQualifiedCoordinates().getAltitude();
                //float speed = location.getSpeed(); 
            }
        }

        public void providerStateChanged(LocationProvider provider, int newState) {
            // MUST implement this.  Should probably do something useful with it as well.
        }
    }




}

please excuse the mess. I have the user lat long hard coded since I do not have GPS functional yet. You can see the SQL query commented out to know how I plan on using the min and max lat and long values.

Any help is appreciated.

Thanks

+1  A: 

If you are targeting OS 4.6 and above, an atan2 method was added to the MathUtilities class. Otherwise, I'd suggest just Googling around until you find an implementation that works. As Matthew Flaschen suggested, compare the results to what you normally get with a standard atan2 function in J2SE or PHP.

Marc Novakowski
A: 

atan2 isn't hard to implement. The definition of what to return based on the 2 arguments is given in the definition. Technically there might be a tiny bit of precision loss from doing the division yourself compared with what might be technically achieveable, but I don't think it will ever matter in rpactice.

DJClayworth