Is there a Math.atan2 substitute for j2ME? Blackberry development
- by Kai
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