How to compute hypotenuse and bearing - android

I got the below code from #DanS at this link how-to-display-a-map-still-image-file-with-a-moving-current-location
onCurrentPosition(Location current){
double hypotenuse = upperLeft.distanceTo(current);
double bearing = upperLeft.bearingTo(current);
double currentDistanceX = Math.cos(bearing) * hypotenuse;
// "percentage to mark the position"
double currentPixelX = (currentDistanceX / upperLeft.distanceTo(lowerRight) * Math.cos(upperLeft.bearingTo(lowerRight))) * mapWidth;
moveIndicatorX(currentPixelX);
}
Here are the values:
current: 41.850033,-87.65005229999997
upperLeft: 41.866514127810355,-87.6720142364502
lowerRight: 41.83397145565242,-87.62824058532715
mapWidth: 512 x 512 px
Here are the calculator online for Location, hypotenuse(Distance), bearing(Azimuths)
convert LatLng to Location format(e.g. 41° 51′ 59.45″ N 87° 40′ 19.25″ W)
compute distance & azimuths from the given Location
I got the results of:
hypotenuse = 2581
bearing = 135.21
currentDistanceX = -2562
currentPixelX = 311.9
Would like to ask you guys to:
to confirm if my computed results are correct.
on how to compute the currentPixelY (the another one point)?
By the way, I am planning to use that to compute the location of a given real life LatLng(current) against with my still image map which bonded the upperLeft and lowerRight corners of the still image into real life LatLng.
If you want to see the actual & expected output and want to easily understand the whole picture. Please refer to this link -> How to mark the current location into a still image map

This is the actual code I'm using, not pseudo code posted previously:
Location upperLeft = new Location("");
upperLeft.setLatitude(41.866514127810355);
upperLeft.setLongitude(-87.6720142364502);
Location lowerRight = new Location("");
lowerRight.setLatitude(41.83397145565242);
lowerRight.setLongitude(-87.62824058532715);
Location current = new Location("");
current.setLatitude(41.850033);
current.setLongitude(-87.65005229999997);
double hypotenuse = upperLeft.distanceTo(current);
double bearing = upperLeft.bearingTo(current);
double currentDistanceX = Math.cos(bearing * Math.PI / 180.0) * hypotenuse;
// "percentage to mark the position"
double totalHypotenuse = upperLeft.distanceTo(lowerRight);
double totalDistanceX = totalHypotenuse * Math.cos(upperLeft.bearingTo(lowerRight) * Math.PI / 180.0);
double currentPixelX = currentDistanceX / totalDistanceX * 512;
System.out.println(currentPixelX); // 259.3345493341548
Your calculated answer looks a bit off.
To calculate Y change copy all the X marked calculations and variables to use Math.sin() instead of Math.cos().

Related

GeoMapping Bearing and Coordinate Calculation for GoogleMaps markers

I'm writing an Android app and integrating GoogleMapsV2 API. I have a series of markers on the map at various locations around an anchor.
I want those markers to converge on the anchor's position incrementally.
I've got a loop running that will call each marker B and from B's position calculate the bearing to the anchor A. I then calculate the destination coordinate for a fixed distance along that bearing and update.
Here are the two functions (taken from an amalgamation of stack posts and a GeoMapping site, for full disclosure) I'm using:
public double calcBearing(double lat1, double lon1, double lat2, double lon2){
double longitude1 = lon1;
double longitude2 = lon2;
double latitude1 = Math.toRadians(lat1);
double latitude2 = Math.toRadians(lat2);
double longDiff= Math.toRadians(longitude2-longitude1);
double y= Math.sin(longDiff)*Math.cos(latitude2);
double x=Math.cos(latitude1)*Math.sin(latitude2)-Math.sin(latitude1)*Math.cos(latitude2)*Math.cos(longDiff);
double calcBearing = (Math.toDegrees(Math.atan2(y, x))+360)%360;
return calcBearing;
}
public Coordinate calcCoordFromPointBearing(double lat1, double lon1, double bearing, double distance){
double rEarth = 6371.01; // Earth's average radius in km
double epsilon = 0.000001; // threshold for floating-point equality
double rLat1 = deg2rad(lat1);
double rLon1 = deg2rad(lon1);
double rbearing = deg2rad(bearing);
double rdistance = distance / rEarth;
double rlat = Math.asin( Math.sin(rLat1) * Math.cos(rdistance) + Math.cos(rLat1) * Math.sin(rdistance) * Math.cos(rbearing) );
double rlon;
if (Math.cos(rlat) == 0 || Math.abs(Math.cos(rlat)) < epsilon) // Endpoint a pole
rlon=rLon1;
else
rlon = ( (rLon1 - Math.asin( Math.sin(rbearing)* Math.sin(rdistance) / Math.cos(rlat) ) + Math.PI ) % (2*Math.PI) ) - Math.PI;
double lat = rad2deg(rlat);
double lon = rad2deg(rlon);
return new Coordinate(lat,lon);
}
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
private double rad2deg(double rad) {
return (rad * 180.0 / Math.PI);
}
In short, I've screwed up the above calculations I believe. The behavior I'm seeing is the markers moving erratically and with a high frequency ending up heading towards two bearings: 90 and 270. As a result, they tend to move away from my anchor instead of towards it.
Can someone help me spot the mistake? I am passing in degrees to both the bearing function and the coordinate calculation function, but I'm converting them immediately to radians for the algorithm and back to degrees for usage elsewhere.
[UPDATE:
Most of the code came from this example:
Calculating coordinates given a bearing and a distance
It looks to me that the output longitude is being normalized to -180 to 180, which I'm plotting on a 360 degree space causing the outputs to head to the bearings 90 and 270. Any suggestions on the trig math change required to fix this?]
probably needs 360.0
double calcBearing = (Math.toDegrees(Math.atan2(y, x))+360.0)%360.0;
This was kindof answered here
You still have another issue. Your not considering any tilt in the map. Why not just animate with the pixels. There won't be too much distortion of curvature. What you have to do is get the pixel position of the marker. You'll have to save the latlon when adding the marker or you have to add the markers with .setAnchor which gives you an offset in pixels. If you have the latlon of the marker placement then you get the point by.
LatLon ll;
Point p = mMap.getProjection().toScreenLocation(ll);
Then you can use code like this to animate the markers. I'm making a marker bounce below by interpolating the y axis. You'll have to interpolate both axi.
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final long duration = 2500;
final Interpolator interpolator = new BounceInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = Math.max(
1 - interpolator.getInterpolation((float) elapsed
/ duration), 0);
marker.setAnchor(0.5f, 1.0f + 6 * t);
if (t > 0.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
The above code is from this question. I apologize for any performance issues you have when you use the above method. But you could still use the pixel positions for a more traditional animation approach.
I've got almost the same formulas as you working in another program where I animate a map to move to the expected location based on a location bearing and speed. The formula is slightly different at the end than yours. I lifted it from here and changed to longer names.
// Define the callback method that receives location updates
#Override
public void onLocationChanged(Location location) {
// Given the bearing, speed, and current location
// calculate what the expected location is traveling for an
// interval that is slightly larger than two times fastest interval of
// the location provider and animate the map movement to the
// expected location over the same slightly larger interval.
// In Theory by using an interval that is slightly larger
// than two times fastest interval of the location provider for the
// animation length a new animation will start before the
// currently running animation finishes. This should ensure a
// smooth animation of the map while traveling under most
// circumstances.
// Negative acceleration (braking)
// should have acceptable map animation because the map
// animation in theory never finishes.
// Note longer intervals, large negative accelerations, just
// braking at the start of an interval may result in the map moving
// backwards. But it will still be animated.
// Some handhelds might not be able to keep up
// TODO CHECK THE age of the location
// location.getSpeed() =meters/second
// interval 1/1000 seconds
// distance in radians km/6371
// changed.
// (location.getSpeed()m/s)(1/1000 interval seconds)( 1/1000 km/m)
// (1/6371 radians/km) = radians/6371000000.0
double expectedDistance = location.getSpeed() * expectedDistMultiplier;
// latitude in Radians
double currentLatitude = Math.toRadians(location.getLatitude());
// longitude in Radians
double longitude1 = Math.toRadians(location.getLongitude());
double bearing;
bearing = (location.hasBearing()) ? Math.toRadians(location
.getBearing()) : 0;
// calculate the expected latitude and longitude based on staring
// location
// , bearing, and distance
double expectedLatitude = Math.asin(Math.sin(currentLatitude)
* Math.cos(expectedDistance) + Math.cos(currentLatitude)
* Math.sin(expectedDistance) * Math.cos(bearing));
double a = Math.atan2(
Math.sin(bearing) * Math.sin(expectedDistance)
* Math.cos(currentLatitude),
Math.cos(expectedDistance) - Math.sin(currentLatitude)
* Math.sin(expectedLatitude));
double expectedLongitude = longitude1 + a;
expectedLongitude = (expectedLongitude + 3 * Math.PI) % (2 * Math.PI)
- Math.PI;
// convert to degrees for the expected destination
double expectedLongitudeDestination = Math.toDegrees(expectedLongitude);
double expectedLatitudeDestination = Math.toDegrees(expectedLatitude);
// log everything for testing.
Log.d("Location", "Bearing in radians" + bearing);
Log.d("Location", "distance in km" + expectedDistance);
Log.d("Location", "Current Latitude = " + location.getLatitude()
+ " Current Longitude = " + location.getLongitude());
Log.d("Location", "New Latitude = " + expectedLatitudeDestination
+ " New Longitude = " + expectedLongitudeDestination);
// build a camera update to animate positioning map to the expected
// destination
LatLng ll = new LatLng(expectedLatitudeDestination,
expectedLongitudeDestination);
CameraPosition.Builder cb = CameraPosition.builder()
.zoom(mMap.getCameraPosition().zoom)
.bearing(mMap.getCameraPosition().bearing)
.tilt(mMap.getCameraPosition().tilt).target(ll);
if (location.hasBearing()) {
cb.bearing(location.getBearing());
}
CameraPosition camera = cb.build();
CameraUpdate update = CameraUpdateFactory.newCameraPosition(camera);
mMap.animateCamera(update, interval, this);
}

Using a GeoPoint location and meters X/Y, how can a Longitude/Latitude be calculated?

I have a little project I've been playing with (Android, GPS, mapping APIs), and I need to figure how to find a longitude/latitude/GeoPoint from a given longitude/latitude/GeoPoint with only knowing the meters/km longitude and latitude. e.g. I want to figure out where a point is from me, that I know is +1000 meters along the longitude and +1000 along the latitude.
It's a little different than the usual GeoPoint/distance questions you'll see, and it's not quite geo fencing radius related as the distance is X,Y meters/kms, and I don't have a bearing. (I could work out a bearing, but I don't have a suitable direct distance)
Basically, if I could reverse GeoPoint.distanceTo() it would do the job for me.
Update
Just a little more background. I'm basically applying a node triangulation idea I had, but the algorithm requires that my inputs be in a map normalized form that's not the same as longitude and latitude. I create a map/grid where 0,0 (the bottom/left) is the left/west and bottom/south most longitude/latitude values from the nodes I'm working with. All the other node X/Y on the map are determined by finding their meters from the 0,0 node's longitude/latitude using GeoPoint.distanceTo(). (note that I find their X/Y by performing distanceTo twice for each node so I have the X and Y meters from 0,0, not a direct line to the node) That distance in meters is fed into the algorithm and new X/Y map points are produced.
And so I need to figure out how to convert distance from a longitude/latitude into another, previously unknown, longitude/latitude.
double startPointLongitude = 23.459821;
double startPointLatitude = 76.998200;
double distanceLongitude = 100; // 100 meters along the longitude
double distanceLatitude = 75; // 75 meters along the latitude
Basically i took the Answer from AlexWien, corrected two things and made it into a java method
private static final double WGS84_RADIUS = 6370997.0;
private static double EarthCircumFence = 2* WGS84_RADIUS * Math.PI;
private static Position getPosition(Position sourcePosition, double mEastWest, double mNorthSouth){
double degreesPerMeterForLat = EarthCircumFence/360.0;
double shrinkFactor = Math.cos((sourcePosition.getLat()*Math.PI/180));
double degreesPerMeterForLon = degreesPerMeterForLat * shrinkFactor;
double newLat = sourcePosition.getLat() + mNorthSouth * (1/degreesPerMeterForLat);
double newLng = sourcePosition.getLng() + mEastWest * (1/degreesPerMeterForLon);
return new Position(newLat, newLng);
}
The distance between two degrees of latitude never change, it is always aprox. 111 km
(The exact value you should caculate by using the WGS84 Earth radius:
EarthCircumFence = 2* WGS84_RADIUS * Math.Pi;
metersPerDegree = (Earth Cirumfence / 360)
With this info you easily can calculate the latitude offset,
just reverse the factor and have:
degreesPerMeterForLat = EarthCircumfenceMeter / 360.0
with longitude its a bit different, the distance between two degrees of longitude shrink
the more you move away from aequator.
shrinkFactor = cos(toRadians(locationLatitude));
compensate now:
degreesPerMeterForLon = degreesPerMeterForLat / shrinkFactor;
Finally
newLatPos = latOld + numMeters * degreesPerMeterForLat;
newLonPos = lonOld + numMeters * degreesPerMeterForLon;
This works for distance offset < 10 - 50 km
Sigh, I posted this like 6 hours ago but it does not appear to have gone through.
Ok, worked it out in spite of most geographical formulas and facts occasionally going over my head. Working with geography is like working with the Gregorian calendar, it makes sense if you program for it all the time, but otherwise it's easy to get confused by an incorrect assumption.
The following except from my app will take a starting GeoPoint's long/lat
/**
* the length of one degree of latitude (and one degree of longitude at equator) in meters.
*/
private static final double DEGREE_DISTANCE_AT_EQUATOR = 111329;
/**
* calculates the x,y in meters from a given starting point's long0, lat0 to a target destination point's long1, lat1.
* #param long0 start point longitude
* #param lat0 start point latitude
* #param long1 end point longitude
* #param lat1 end point latitude
* #return
*/
public static Pair<Double, Double> xyFromLongLat(int long0, int lat0, int long1, int lat1) {
double x = (long1 / 1E6 - long0 / 1E6) * longitudeDistanceAtLatitude(lat0 / 1E6);
double y = (lat1 / 1E6 - lat0 / 1E6) * DEGREE_DISTANCE_AT_EQUATOR;
return new Pair<Double, Double>(x, y);
}
/**
* calculates longitude and latitude from a given starting point, with only the X/Y meters
* #param long0
* #param lat0
* #param x
* #param y
* #return
*/
public static Pair<Double, Double> longLatFromXY(int long0, int lat0, double x, double y) {
double lat1 = (y / DEGREE_DISTANCE_AT_EQUATOR) + (lat0 / 1E6);
double long1 = x / longitudeDistanceAtLatitude(lat0) + (long0 / 1E6);
return new Pair<Double, Double>(lat1, long1);
}

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.

Calculate longitude from distance

Found a plenty of answers to question how to calculate distance by lat/lon and nothing for a "reverse" problem.
I have a displacment in X and Y and a GPS point (lat/lon), yet need to calc coordinates for a new point.
Using formula:
double deltaLat = dy / EARTH_RADIUS;
double deltaLon = dx / EARTH_RADIUS;
double lat = locLat + Math.signum(dy) * Math.toDegrees(deltaLat); // formula correct
double lon = locLon + Math.signum(dx) * Math.toDegrees(deltaLon);
It's accurate for calculating latitude, but for longitude I get about 10–15% error.
Does anyone have the same issue? Any possible formulas to calculate longitude by displacement?
The reason you're getting 10-15% error in longitude is because for longitude you cannot use the earth's radius to compute your displacement. Instead, you need to use the radius of the "circle" at the corresponding latitude. Therefore using your formula your longitude calculations should be more like
double deltaLon = dx / (EARTH_RADIUS * cos(locLat))
However this may give you undesired results around the poles, as cos(locLat) will get close to 0, so you may want to have some special cases for the poles (or even around them). Logically, if you think about, if you're at the pole, moving any distance along the x axis will not get your anywhere anyway.
Simplest but not the best solution is:
double deltaLat = dy / EARTH_RADIUS; // up-down
double deltaLon = dx / EARTH_RADIUS ; // left-right
double lat = locLat + Math.signum(dy) * Math.toDegrees(deltaLat); // formula correct
double lon = locLon + Math.signum(dx) * Math.toDegrees(deltaLon * 1.195);

Determinate a Geopoint from another, a distance, and a polar angle

I'm working on an Android app that uses Geopoints and I want to determinate a Geopoint from another Geopoint, a distance (in any format) and a polar angle. For example, I want to get coordinates of a place 100 meters in the North-North-East (22,5 degres) of my location got by the GPS in my phone.
The only method I've found is Location.distanceBetween(...).
Implementation for Android. This code is great for Unit Testing in your aplication:
public double radiansFromDegrees(double degrees)
{
return degrees * (Math.PI/180.0);
}
public double degreesFromRadians(double radians)
{
return radians * (180.0/Math.PI);
}
public Location locationFromLocation(Location fromLocation, double distance, double bearingDegrees)
{
double distanceKm = distance / 1000.0;
double distanceRadians = distanceKm / 6371.0;
//6,371 = Earth's radius in km
double bearingRadians = this.radiansFromDegrees(bearingDegrees);
double fromLatRadians = this.radiansFromDegrees(fromLocation.getLatitude());
double fromLonRadians = this.radiansFromDegrees(fromLocation.getLongitude());
double toLatRadians = Math.asin( Math.sin(fromLatRadians) * Math.cos(distanceRadians)
+ Math.cos(fromLatRadians) * Math.sin(distanceRadians) * Math.cos(bearingRadians) );
double toLonRadians = fromLonRadians + Math.atan2(Math.sin(bearingRadians)
* Math.sin(distanceRadians) * Math.cos(fromLatRadians), Math.cos(distanceRadians)
- Math.sin(fromLatRadians) * Math.sin(toLatRadians));
// adjust toLonRadians to be in the range -180 to +180...
toLonRadians = ((toLonRadians + 3*Math.PI) % (2*Math.PI) ) - Math.PI;
Location result = new Location(LocationManager.GPS_PROVIDER);
result.setLatitude(this.degreesFromRadians(toLatRadians));
result.setLongitude(this.degreesFromRadians(toLonRadians));
return result;
}
Take a look at great-circle formulas: http://en.wikipedia.org/wiki/Great-circle_distance
This should give You some hints on how to calculate the distances.
For a point in a given distance and heading, check http://williams.best.vwh.net/avform.htm#LL
Those formulas look quite complicated, but are easy to implement ;)

Categories

Resources