Making a Compass point to a particular location in Unity - android

I'm trying to make a compass that points to a custom location in a Unity program. However it's completely off, it points in generally the wrong direction and I can't quite seem to figure out what is wrong with my logic.
I'm using the curved geometry bearing equation from here: http://www.yourhomenow.com/house/haversine.html
Get current location.
Calculate bearing from current location to target location.
Rotate screen compass relative to true north.
// Calculate bearing between current location and target location
// Get current GPS location
float lat1 = Input.location.lastData.latitude;
float lon1 = Input.location.lastData.longitude;
float lat2 = TargetLatitude;
float dLon = TargetLongitude - lon1;
// Calculate bearing
var y = Mathf.Sin(dLon) * Mathf.Cos(lat2);
var x = Mathf.Cos(lat1) * Mathf.Sin(lat2) -
Mathf.Sin(lat1) * Mathf.Cos(lat2) * Mathf.Cos(dLon);
var brng = ((Mathf.Atan2(y, x)*(180.0f/Mathf.PI)) + 360.0f) % 360.0f;
Dump.text = brng.ToString();
// Rotate the to target location relative to north. Z axis pointing out of screen.
transform.eulerAngles = new Vector3(0, 0, Mathf.MoveTowardsAngle(transform.localEulerAngles.z, Input.compass.trueHeading + brng, COMPASS_MAXDELTA));
If I remove the bearing offset in that code, it points north enough for a digital compass.

Make sure that all of your angles are in radians rather than degrees.
Using Mathf.Deg2Rad in cases such as Mathf.Sin(dLon * Mathf.Deg2Rad) should yield the correct result. The Haversine formula only works for angles in radians.

Related

Calculate the opposite of the current android GPS walking direction

I'm having this small code snippet here. What I'm looking for is to get the current GPS walking direction (bearing) of the user and then get the opposite walking direction.
I seem to be missing some crucial information here since the code seems to mostly work when I walk North East and West but fails when I walk South.
Any idea how to fix my snippet?
private static Location lastValidLocation;
private float getOppositeGPSMovementDirection(Location lastLocation){
float bearing;
if (!lastLocation.hasBearing){
//Use bearing of the last Location with a valid bearing.
bearing = lastValidLocation.getBearing();
}else{
lastValidLocation = lastLocation;
bearing = lastLocation.getBearing();
}
//Calculate backwards direction
int direction = (int) (bearing - 180);
direction = direction<0?direction*-1:direction;
return direction;
}
Your math is faulty. If you're at 10 degrees, your opposite is 190. Your math would give it 170. The correct math is:
direction = (bearing + 180) % 360;
Basically, your assumption that you can multiple a negative angle by -1 to get the correct angle is wrong.

How to point my compass to Mecca?

I took the code of compass from this link.
http://www.androidcode.ninja/android-compass-code-example/
How to set mecca location on my compass??
What do i need to do to point my compass to Mecca?
You need to determine your location relatively to Mecca, so you will need Location permission and implement Location Updates.
You can implement that using: Receveing location updates
With this information you can determine where Mecca would be on your compass and set it accordingly. To determine the angle, use this code:
private double angleFromCoordinate(double lat1, double long1, double lat2,
double long2) {
double dLon = (long2 - long1);
double y = Math.sin(dLon) * Math.cos(lat2);
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
* Math.cos(lat2) * Math.cos(dLon);
double brng = Math.atan2(y, x);
brng = Math.toDegrees(brng);
brng = (brng + 360) % 360;
brng = 360 - brng; // count degrees counter-clockwise - remove to make clockwise
return brng;
}
from: Calculate angle between two Latitude/Longitude points
Well for very few words that's a long question. The answer is to extend the OnSensorChanged code from the example to calculate the heading to Mecca from the current location and adjust the heading based on the current direction the sensor is facing in. You'll need the current location co-ordinates from GPS and the fixed co-ordintates for Mecca, Calculate the direction relative to North (the way the compass is designed to point) and then adjust by the current heading.

convert latitude and longitude to world coordinates in opengl in right handed

I've an equirectangular projection of an image, which I load as a panoramic image where my camera is placed at origin looking at -z. My coordinate system is right handed, i.e x is on right, y is up (north) and z is coming out of screen.
What I want to do is take the selected pixel and convert it to world coordinates xyz.
The formula that I'm using to get the latitude and longitude are:
double centerX = totalWidth/2;
double centerY = totalHeight/2;
double latScaling = centerY / 90;
double lonScaling = centerX / 180;
double longitude = (pixelX-centerX)/lonScaling;
double latitude = -(pixelY-centerY)/latScaling;
totalWidth and totalHeight is the width and height of the equirectangular image and pixelX and pixelY is the selected pixel coordinates.
Further I'm using these lat/long to get the world coordinates as:
double x = sphereRadius * Math.sin(latRadians) * Math.sin(longRadians);
double y = sphereRadius * Math.cos(latRadians);
double z = sphereRadius * Math.cos(longRadians) * Math.sin(latRadians);
sphereRadius is 18 which is equal to the radius of opengl sphere on which the image is finally drawn. The camera is at the origin of this sphere.
Please can somebody check if my approach is correct and if the formula's used are correct for a right handed coordinate system.
Using the above formulae I get x=0,y=18,z=0 for latitude = 0 and longitude = 0 which is not expected result. Have I missed something?
EDIT: The formula that seems to be working for me is :
//flip Y axis
latRadians = Math.PI/2 - latRadians;
double x = sphereRadius * Math.sin(latRadians) * Math.sin(longRadians);
double y = -sphereRadius * Math.cos(latRadians);
double z = -sphereRadius * Math.sin(latRadians) * Math.cos(longRadians);
But there's still some offset(around 850 pixels) and the deviation is minimum at equator and prime meridian, since it contains the true scale value. I think I need to calculate the offset through angles between up vector and right vector of camera. Can somebody correct me?
There's a flat equirectangular projection of which I take pixel xy position. Then this equirectangular is wrapped on the inside of sphere and my camera is at origin of this sphere. I need to calculate the world co-ordinates from pixel xy. Hope this clarifies some doubts.
My approach was slightly wrong. Instead of searching for latitude longitude mapping I had to compute UV mapping of the texture. The UV mapping is slightly different from lat/long mapping. Instead of a value between -PI and PI the UV mapping is between 0.0-1.0. The texture is divided into sectors and rings. You can find the logic to generate a sphere body here
This is how I solved this problem:
generated the UV mapping from xy position of pixel
double u = pixelX/totalWidth;
double v = 1 - pixelY/totalHeight;
And used the following formula to convert to xyz coordinates of sphere
double theta = 2 * Math.PI * u; //sector
double phi = Math.PI * v; //ring
double x = Math.cos(theta) * Math.sin(phi) * sphereRadius;
double y = -Math.sin(-Math.PI/2 + phi) * sphereRadius;
double z = Math.sin(theta) * Math.sin(phi) * sphereRadius;
The sphere has poles on y axis north on +ve y and south on -ve y, where camera is at the origin of this sphere with up-vector as 0,1,0 and right vector as 1,0,0 and look-at with 0,0,-1

Android - How to use Helmert Transformation to convert gps to screen XY on CUSTOM MAP

I have built an activity that takes a custom image that I use for a map and then knowing the gps at the top left and bottom right I plot a gps on top of the map. It works pretty good but I would like to get the accuracy up. I know its off because as I log the device location and plug it into the google map its actually more accurate than I am representing on my custom map.
So....being that I have the top left and bottom right gps of the map and have mapped them to the corresponding pixel coordinates how can I accurately plot the devices gps into pixels accurately using Helmert Transformation.
EDIT:
I am currently using this to plot the gps of the device to the screen.
public double getCurrentPixelY(Location upperLeft, Location lowerRight, Location current) {
double hypotenuse = upperLeft.distanceTo(current);
double bearing = upperLeft.bearingTo(current);
double currentDistanceY = Math.cos(bearing * Math.PI / OneEightyDeg) * hypotenuse;
// "percentage to mark the position"
double totalHypotenuse = upperLeft.distanceTo(lowerRight);
double totalDistanceY = totalHypotenuse * Math.cos(upperLeft.bearingTo(lowerRight) * Math.PI / OneEightyDeg);
double currentPixelY = currentDistanceY / totalDistanceY * ImageSizeH;
return currentPixelY;
}
public double getCurrentPixelX(Location upperLeft, Location lowerRight, Location current) {
double hypotenuse = upperLeft.distanceTo(current);
double bearing = upperLeft.bearingTo(current);
double currentDistanceX = Math.sin(bearing * Math.PI / OneEightyDeg) * hypotenuse;
// "percentage to mark the position"
double totalHypotenuse = upperLeft.distanceTo(lowerRight);
double totalDistanceX = totalHypotenuse * Math.sin(upperLeft.bearingTo(lowerRight) * Math.PI / OneEightyDeg);
double currentPixelX = currentDistanceX / totalDistanceX * ImageSizeW;
return currentPixelX;
}
I know I need to make an adjustment in there somewhere but looking at the helmert transformation I cant figure where to start implementing it with my existing code.
EDIT:
After looking at some more stuff online I can see that using the great circle formula might help. Heres a link to what Im looking at implementing
http://introcs.cs.princeton.edu/java/12types/GreatCircle.java.html
Here is source code that calculates the helmert coefficients:
http://helmparms3d.sourceforge.net/
Maybe there is a simpler approach (there is also a so called 2d helmert transormation for small maps, like your picture)
Using that code you get the helmert coefficients, these coefficients is a 3x3 matrix. so you need code that is able to multiply a vector with a matrix.
The 3d graphic routines have such matrix multiplications.

Calculating the angle between the user and POI (Points of Interest) in android

I want to make a function that calculate the angle between the user and one point of interest (POI) with reference to the true north.
I already have the longitude and latitude of my position and the POI position and now i need the angle between them.
Here is what i have:
private float calcAzimuth1(float lat0, float long0, float lat1, float long1)
{
//user's latitude and longitude
float userLat = (float) ((lat0 * ((float)Math.PI))/180);
float userLong = (float) ((long0 * ((float)Math.PI))/180);
//POI's latitude and longitude
float latT1 = (float) ((lat1 * ((float)Math.PI))/180);
float longT1 = (float) ((long1 * ((float)Math.PI))/180);
//angle between them
float angle=??}
Check bearingTo function of Location class
http://developer.android.com/reference/android/location/Location.html#bearingTo(android.location.Location)
You can create two Location objects from your Lat/Longs and then get the bearing between them.
The other thing you might need is to calculate the device orientation and then combine them together. That requires listening to Sensors and calculating the North from the information received. check the answer here:
Compass readings on SGS III

Categories

Resources