Find nearest KML point to a Geo-location - android

In my Android app I am showing a KML on Google Map. I am also showing device location on the Map.
Now I want to find the point/line on KML which is closest to the device location on the Map. How can achieve that?
Also, I want to know if any KML point/line is within 10 meters of device location.

Solved. I followed the following steps to solve the 2nd part:
Added function to detect if line-segment collides with circle:
ref: https://stackoverflow.com/a/21989358/1397821
Java converted function:
static boolean checkLineSegmentCircleIntersection(double x1, double y1, double x2 , double y2, double xc, double yc, double r) {
double xd = 0.0;
double yd = 0.0;
double t = 0.0;
double d = 0.0;
double dx_2_1 = 0.0;
double dy_2_1 = 0.0;
dx_2_1 = x2 - x1;
dy_2_1 = y2 - y1;
t = ((yc - y1) * dy_2_1 + (xc - x1) * dx_2_1) / (dy_2_1 * dy_2_1 + dx_2_1 * dx_2_1);
if( 0 <= t && t <=1) {
xd = x1 + t * dx_2_1;
yd = y1 + t * dy_2_1;
d = Math.sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc));
return d <= r;
}
else {
d = Math.sqrt((xc - x1) * (xc - x1) + (yc - y1) * (yc - y1));
if (d <= r)
return true;
else {
d = Math.sqrt((xc - x2) * (xc - x2) + (yc - y2) * (yc - y2));
if (d <= r)
return true;
else
return false;
}
}
}
Parsed the KML coordinates and passed the coordinates of line segments to this function, like :
boolean lineInRadius = checkLineSegmentCircleIntersection(points.get(i - 1).latitude, points.get(i - 1).longitude,
points.get(i).latitude, points.get(i).longitude, latDecimal, lngDecimal, RADIUS);
Note: your radius can be aprx 0.000009 for 1 meter (https://stackoverflow.com/a/39540339/1397821). This is not exact radius, it'll be oval.
To solve the 1st part, you can edit the above function and find the minimum distance. Check the line d <= r where distance is compared with radius.

Related

What could a function look like that receives three locations and three radius and outputs one coordinate showing the overlapping?

I have three gps locations as double lat and lng. I have three radius that correspond to each of the lat and lng values. The radius form circles around the locations. I want to determine the one point where all three circles overlap.
My starting point:
(x−lat_1)^2+(y−lng_1)^2=r_1^2
(x−lat_2)^2+(y−lng_2)^2=r_2^2
(x−lat_3)^2+(y−lng_3)^2=r_3^2
But here I am stuck - not only is that system of equations over-determined, it is also unclear, how to mix up degrees, minutes and seconds with a radius in meters.
What could a function(pseudocode is enough) look like that receives three locations and three radius and outputs one coordinate showing the overlapping.
Speaking of which, there needs to be some tolerance, as neither the radius nor the locations are too precise.
Take a look at this question:
Find intersecting point of three circles programmatically
I'm posting here the code that does what you need:
private static final double EPSILON = 0.000001;
private boolean calculateThreeCircleIntersection(double x0, double y0, double r0,
double x1, double y1, double r1,
double x2, double y2, double r2)
{
double a, dx, dy, d, h, rx, ry;
double point2_x, point2_y;
/* dx and dy are the vertical and horizontal distances between
* the circle centers.
*/
dx = x1 - x0;
dy = y1 - y0;
/* Determine the straight-line distance between the centers. */
d = Math.sqrt((dy*dy) + (dx*dx));
/* Check for solvability. */
if (d > (r0 + r1))
{
/* no solution. circles do not intersect. */
return false;
}
if (d < Math.abs(r0 - r1))
{
/* no solution. one circle is contained in the other */
return false;
}
/* 'point 2' is the point where the line through the circle
* intersection points crosses the line between the circle
* centers.
*/
/* Determine the distance from point 0 to point 2. */
a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
/* Determine the coordinates of point 2. */
point2_x = x0 + (dx * a/d);
point2_y = y0 + (dy * a/d);
/* Determine the distance from point 2 to either of the
* intersection points.
*/
h = Math.sqrt((r0*r0) - (a*a));
/* Now determine the offsets of the intersection points from
* point 2.
*/
rx = -dy * (h/d);
ry = dx * (h/d);
/* Determine the absolute intersection points. */
double intersectionPoint1_x = point2_x + rx;
double intersectionPoint2_x = point2_x - rx;
double intersectionPoint1_y = point2_y + ry;
double intersectionPoint2_y = point2_y - ry;
Log.d("INTERSECTION Circle1 AND Circle2:", "(" + intersectionPoint1_x + "," + intersectionPoint1_y + ")" + " AND (" + intersectionPoint2_x + "," + intersectionPoint2_y + ")");
/* Lets determine if circle 3 intersects at either of the above intersection points. */
dx = intersectionPoint1_x - x2;
dy = intersectionPoint1_y - y2;
double d1 = Math.sqrt((dy*dy) + (dx*dx));
dx = intersectionPoint2_x - x2;
dy = intersectionPoint2_y - y2;
double d2 = Math.sqrt((dy*dy) + (dx*dx));
if(Math.abs(d1 - r2) < EPSILON) {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "(" + intersectionPoint1_x + "," + intersectionPoint1_y + ")");
}
else if(Math.abs(d2 - r2) < EPSILON) {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "(" + intersectionPoint2_x + "," + intersectionPoint2_y + ")"); //here was an error
}
else {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "NONE");
}
return true;
}
Usage:
calculateThreeCircleIntersection(-2.0, 0.0, 2.0, // circle 1 (center_x, center_y, radius)
1.0, 0.0, 1.0, // circle 2 (center_x, center_y, radius)
0.0, 4.0, 4.0);// circle 3 (center_x, center_y, radius)
As you said, you probably need to do some unit conversion here. There is some complicated formula that calculates distance between two geolocations, so you need to reverse it to get meters from radian based distance.
Here you may find implementations of this calculation and try to reverse it:
Calculate distance between two latitude-longitude points? (Haversine formula)

Algorithm to draw arrowhead at the end of arbitrary line in an Android custom View

I've been trying to come up with an algorithm to draw an arrow in a custom View, using Path, but I haven't figured out how to get the coordinates of the arrowhead tips. The line startpoint and endpoint coordinates are arbitrary, the angle of the arrowhead relative to the line and the length of the arrowhead are fixed.
I think I have to use trigonometry somehow, but I'm not sure how.
My friend came up with a math equation, which I have translated into java code here:
public static void calculateArrowHead(Point start, Point end, double angleInDeg, double tipLength){
double x1 = end.getX();
double x2 = start.getX();
double y1 = end.getY();
double y2 = start.getY();
double alpha = Math.toRadians(angleInDeg);
double l1 = Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2)); // length of the arrow line
double l2 = tipLength;
double a = Math.pow(y2-y1, 2) + Math.pow(x2-x1, 2);
double b = -2 * l1 * l2 * Math.cos(alpha) * (y2 - y1);
double c = Math.pow(l1, 2) * Math.pow(l2, 2) * Math.pow(Math.cos(alpha), 2) - Math.pow(l2, 2) * Math.pow(x2-x1, 2);
double s2a = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
double s2b = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
double s1a = (l1 * l2 * Math.cos(alpha) - s2a * (y2 - y1)) / (x2-x1);
double s1b = (l1 * l2 * Math.cos(alpha) - s2b * (y2 - y1)) / (x2-x1);
double x3a = s1a + x1;
double y3a = s2a + y1;
double x3b = s1b + x1;
double y3b = s2b + y1;
System.out.println("(A) x:" + (int)x3a + "; y:" + (int)y3a);
System.out.println("(B) x:" + (int)x3b + "; y:" + (int)y3b);
}
I haven't tested it thoroughly, but for the first few tests, it appears to be correct.

How to add Polygon touch event handler to many polygons in Google Maps Android [duplicate]

I'm trying to figure out how best to do this, I have a map with one Polygon drawn on it. Since it doesn't seem as though the Google Maps API V2 has a touch detection on a Polygon. I was wonder if it is possible to detect whether the touch point is inside the Polygon? If so then how, my main goal is to outline a state on a map and when the user taps that state it will show more details inside a custom view. As of now I am able to capture the MapOnClick of the map but when the user taps inside the Polygon I want the polygon.getID() set on the Toast. I am a newbie so I apologize if I am not clear enough.
googleMap.setOnMapClickListener(new OnMapClickListener()
{
public void onMapClick(LatLng point)
{
boolean checkPoly = true;
Toast.makeText(MainActivity.this,"The Location is outside of the Area", Toast.LENGTH_LONG).show();
}
});
}
}
catch (Exception e) {
Log.e("APP","Failed", e);
}
Ok this is what I have semi-working so far
private boolean rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if (aY > bY) {
aX = vertB.longitude;
aY = vertB.latitude;
bX = vertA.longitude;
bX = vertA.latitude;
}
System.out.println("aY: "+aY+" aX : "+aX);
System.out.println("bY: "+bY+" bX : "+bX);
if (pX < 0) pX += 360;
if (aX < 0) aX += 360;
if (bX < 0) bX += 360;
if (pY == aY || pY == bY) pY += 0.00000001;
if ((pY > bY || pY < aY) || (pX > Math.max(aX, bX))) return false;
if (pX < Math.min(aX, bX))
return true;
// }
double m = (aX != bX) ? ((bY - aY) / (bX - aX)) : aX;
double bee = (aX != pX) ? ((pY - aY) / (pX - aX)) : aX;
double x = (pY - bee) / m;
return x > pX;
}
}
The issue that I am having is the touch is true to the left of each polygon until it reaches another one. What's wrong with my algorithm that would cause this issue? Any help would be appreciated.
The problem you're trying to solve is the Point in Polygon test.
To help visualize the concept of Ray Casting:
Draw a Polygon on a piece of paper. Then, starting at any random point, draw a straight line to the right of the page. If your line intersected with your polygon an odd number of times, this means your starting point was inside the Polygon.
So, how do you do that in code?
Your polygon is comprised of a list of vertices: ArrayList<Geopoint> vertices. You need to look at each Line Segment individually, and see if your Ray intersects it
private boolean isPointInPolygon(Geopoint tap, ArrayList<Geopoint> vertices) {
int intersectCount = 0;
for(int j=0; j<vertices.size()-1; j++) {
if( rayCastIntersect(tap, vertices.get(j), vertices.get(j+1)) ) {
intersectCount++;
}
}
return (intersectCount%2) == 1); // odd = inside, even = outside;
}
private boolean rayCastIntersect(Geopoint tap, Geopoint vertA, Geopoint vertB) {
double aY = vertA.getLatitude();
double bY = vertB.getLatitude();
double aX = vertA.getLongitude();
double bX = vertB.getLongitude();
double pY = tap.getLatitude();
double pX = tap.getLongitude();
if ( (aY>pY && bY>pY) || (aY<pY && bY<pY) || (aX<pX && bX<pX) ) {
return false; // a and b can't both be above or below pt.y, and a or b must be east of pt.x
}
double m = (aY-bY) / (aX-bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
The Google Maps Support library now has a static method that does this check for you:
PolyUtil.containsLocation(LatLng point, List<LatLng>polygon, boolean geodesic);
Although the docs don't mention it explicitly in the guide the method is there
Maps Support Library docs
With the release of Google Play Services 8.4.0, the Maps API has included support for adding an OnPolygonClickListener to Polygons. Both polygons, polylines and overlays support similar events.
You just need to call GoogleMap.setOnPolygonClickListener(OnPolygonClickListener listener) to set it up, and correspondingly for the other listeners (setOnPolylineClickListener, &c):
map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
#Override
public void onPolygonClick(Polygon polygon) {
// Handle click ...
}
});
Although a bit late, it solves this use case quite nicely.
Though user1504495 has answered in short as I have used it. But instead of using whole Map Utility Library Use this methods.
From your activity class pass params accordingly:
if (area.containsLocation(Touchablelatlong, listLatlong, true))
isMarkerINSide = true;
else
isMarkerINSide = false;
and put following in a Separate class :
/**
* Computes whether the given point lies inside the specified polygon.
* The polygon is always cosidered closed, regardless of whether the last point equals
* the first or not.
* Inside is defined as not containing the South Pole -- the South Pole is always outside.
* The polygon is formed of great circle segments if geodesic is true, and of rhumb
* (loxodromic) segments otherwise.
*/
public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
final int size = polygon.size();
if (size == 0) {
return false;
}
double lat3 = toRadians(point.latitude);
double lng3 = toRadians(point.longitude);
LatLng prev = polygon.get(size - 1);
double lat1 = toRadians(prev.latitude);
double lng1 = toRadians(prev.longitude);
int nIntersect = 0;
for (LatLng point2 : polygon) {
double dLng3 = wrap(lng3 - lng1, -PI, PI);
// Special case: point equal to vertex is inside.
if (lat3 == lat1 && dLng3 == 0) {
return true;
}
double lat2 = toRadians(point2.latitude);
double lng2 = toRadians(point2.longitude);
// Offset longitudes by -lng1.
if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
++nIntersect;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}
/**
* Wraps the given value into the inclusive-exclusive interval between min and max.
* #param n The value to wrap.
* #param min The minimum.
* #param max The maximum.
*/
static double wrap(double n, double min, double max) {
return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
}
/**
* Returns the non-negative remainder of x / m.
* #param x The operand.
* #param m The modulus.
*/
static double mod(double x, double m) {
return ((x % m) + m) % m;
}
/**
* Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
* (lat1, lng1) to (lat2, lng2).
* Longitudes are offset by -lng1; the implicit lng1 becomes 0.
*/
private static boolean intersects(double lat1, double lat2, double lng2,
double lat3, double lng3, boolean geodesic) {
// Both ends on the same side of lng3.
if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
return false;
}
// Point is South Pole.
if (lat3 <= -PI/2) {
return false;
}
// Any segment end is a pole.
if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
return false;
}
if (lng2 <= -PI) {
return false;
}
double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
// Northern hemisphere and point under lat-lng line.
if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
return false;
}
// Southern hemisphere and point above lat-lng line.
if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
return true;
}
// North Pole.
if (lat3 >= PI/2) {
return true;
}
// Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
// Compare through a strictly-increasing function (tan() or mercator()) as convenient.
return geodesic ?
tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
}
/**
* Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0.
* See http://williams.best.vwh.net/avform.htm .
*/
private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
}
/**
* Returns mercator Y corresponding to latitude.
* See http://en.wikipedia.org/wiki/Mercator_projection .
*/
static double mercator(double lat) {
return log(tan(lat * 0.5 + PI/4));
}
/**
* Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
*/
private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
}
Here's a full working example to know if a touch happened on a polygon. Some of the answers are more complicated than they need to be. This solution uses the "android-maps-utils"
// compile 'com.google.maps.android:android-maps-utils:0.3.4'
private ArrayList<Polygon> polygonList = new ArrayList<>();
private void addMyPolygons() {
PolygonOptions options = new PolygonOptions();
// TODO: make your polygon's however you want
Polygon polygon = googleMap.addPolygon(options);
polygonList.add(polygon);
}
#Override
public void onMapClick(LatLng point) {
boolean contains = false;
for (Polygon p : polygonList) {
contains = PolyUtil.containsLocation(point, p.getPoints(), false);
if (contains) break;
}
Toast.makeText(getActivity(), "Click in polygon? "
+ contains, Toast.LENGTH_SHORT).show();
}
#Override
protected void onMapReady(View view, Bundle savedInstanceState) {
googleMap.setOnMapClickListener(this);
addMyPolygons();
}
I know I am posting this very late but I had some issue with the answer posted here, so I studied both the top answers and an article (which I think is the origin of this method) and modified Matt Answer to compile something that works best for me.
Problem with Matt Answer: It doesn't calculate the last line of polygon (i.e. one created by the last vertex and the first vertex)
Problem with Dwill Answer: It seems complex and daunting especially when you are already frustrated on how to make things work
Other checks I have added:
Checked if a polygon is actually created
Checked if any side of polygon is parallel to y-axis
I have tried to comment and explain as much I as I could hope this would be helpful for someone
One more thing, this is written in Dart and mainly focused on finding if current position is inside a geofence.
Future<bool> checkIfLocationIsInsideBoundary({
required LatLng positionToCheck,
required List<LatLng> boundaryVertices,
}) async {
// If there are less than 3 points then there will be no polygon
if (boundaryVertices.length < 3) return false;
int intersectCount = 0;
// Check Ray-cast for lines created by all the vertices in our List
for (int j = 0; j < boundaryVertices.length - 1; j++) {
if (_rayCastIntersect(
positionToCheck,
boundaryVertices[j],
boundaryVertices[j + 1],
)) {
intersectCount++;
}
}
// Check for line created by the last vertex and the first vertex of the List
if (_rayCastIntersect(
positionToCheck,
boundaryVertices.last,
boundaryVertices.first,
)) {
intersectCount++;
}
// If our point is inside the polygon they will always intersect odd number of
// times, else they will intersect even number of times
return (intersectCount % 2) == 1; // odd = inside, even = outside
}
bool _rayCastIntersect(LatLng point, LatLng vertA, LatLng vertB) {
final double aY = vertA.latitude;
final double bY = vertB.latitude;
final double aX = vertA.longitude;
final double bX = vertB.longitude;
final double pY = point.latitude;
final double pX = point.longitude;
// If vertices A and B are both above our point P then obviously the line made
// by A and B cannot intersect with ray-cast of P. Note: Only y-coordinates of
// each points can be used to check this.
if (aY > pY && bY > pY) return false;
// If vertices A and B are both below our point P then obviously the line made
// by A and B cannot intersect with ray-cast of P. Note: Only y-coordinates of
// each points can be used to check this.
if (aY < pY && bY < pY) return false;
// Since we will be casting ray on east side from our point P, at least one of
// the vertex (either A or B) must be east of P else line made by A nd B
// cannot intersect with ray-cast of P. Note: Only x-coordinates of each
// points can be used to check this.
if (aY < pY && bY < pY) return false;
// If line made by vertices is parallel to Y-axis then we will get
// 'Divided by zero` exception when calculating slope. In such case we can
// only check if the line is on the east or the west relative to our point. If
// it is on the east we count is as intersection. Note: we can be sure our
// ray-cast will intersect the line because it is a vertical line, our
// ray-cast is horizontal and finally we already made sure that both the
// vertices are neither above nor below our point. Finally, since `aX == bX`
// we can check if either aX or bX is on the right/east of pX
if (aX == bX) return aX > pX;
// Calculate slope of the line `m` made by vertices A and B using the formula
// `m = (y2-y1) / (x2-x1)`
final double m = (aY - bY) / (aX - bX); // Rise over run
// Calculate the value of y-intersect `b` using the equation of line
final double b = aY - (aX * m); // y = mx + b => b = y - mx
// Now we translate our point P along X-axis such that it intersects our line.
// This means we can pluck y-coordinate of our point P into the equation of
// our line and calculate a new x-coordinate
final double x = (pY - b) / m; // y = mx + b => x = (y - b) / m
// Till now we have only calculated this new translated point but we don't
// know if this point was translated towards west(left) of towards
// east(right). This can be determined in the same way as we have done above,
// if the x-coordinate of this new point is greater than x-coordinate of our
// original point then it has shifted east, which means it has intersected our
// line
return x > pX;
}
Just for consistency - onMapClick is not called when user taps on a polygon (or other overlay), and it's mentioned in javadoc.
I made a workaround to intercept taps events before MapFragment handles them, and project point to map coordinates and check if the point is inside any polygon, as suggested in other answer.
See more details here

Calculate bearing between two locations (lat, long)

I'm trying to develop my own augmented reality engine.
Searching on internet, I've found this useful tutorial. Reading it I see that the important thing is bearing between user location, point location and north.
The following picture is from that tutorial.
Following it, I wrote an Objective-C method to obtain beta:
+ (float) calculateBetaFrom:(CLLocationCoordinate2D)user to:(CLLocationCoordinate2D)destination
{
double beta = 0;
double a, b = 0;
a = destination.latitude - user.latitude;
b = destination.longitude - user.longitude;
beta = atan2(a, b) * 180.0 / M_PI;
if (beta < 0.0)
beta += 360.0;
else if (beta > 360.0)
beta -= 360;
return beta;
}
But, when I try it, it doesn't work very well.
So, I checked iPhone AR Toolkit, to see how it works (I've been working with this toolkit, but it is so big for me).
And, in ARGeoCoordinate.m there is another implementation of how to obtain beta:
- (float)angleFromCoordinate:(CLLocationCoordinate2D)first toCoordinate:(CLLocationCoordinate2D)second {
float longitudinalDifference = second.longitude - first.longitude;
float latitudinalDifference = second.latitude - first.latitude;
float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);
if (longitudinalDifference > 0)
return possibleAzimuth;
else if (longitudinalDifference < 0)
return possibleAzimuth + M_PI;
else if (latitudinalDifference < 0)
return M_PI;
return 0.0f;
}
It uses this formula:
float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);
Why is (M_PI * .5f) in this formula? I don't understand it.
And continue searching, I've found another page talking about how to calculate distance and bearing of 2 locations. In this page there is another implementation:
/**
* Returns the (initial) bearing from this point to the supplied point, in degrees
* see http://williams.best.vwh.net/avform.htm#Crs
*
* #param {LatLon} point: Latitude/longitude of destination point
* #returns {Number} Initial bearing in degrees from North
*/
LatLon.prototype.bearingTo = function(point) {
var lat1 = this._lat.toRad(), lat2 = point._lat.toRad();
var dLon = (point._lon-this._lon).toRad();
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
return (brng.toDeg()+360) % 360;
}
Which one is the right one?
Calculate bearing
//Source
JSONObject source = step.getJSONObject("start_location");
double lat1 = Double.parseDouble(source.getString("lat"));
double lng1 = Double.parseDouble(source.getString("lng"));
// destination
JSONObject destination = step.getJSONObject("end_location");
double lat2 = Double.parseDouble(destination.getString("lat"));
double lng2 = Double.parseDouble(destination.getString("lng"));
double dLon = (lng2-lng1);
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.toDegrees((Math.atan2(y, x)));
brng = (360 - ((brng + 360) % 360));
Convert Degrees into Radians
Radians = Degrees * PI / 180
Convert Radians into Degrees
Degrees = Radians * 180 / PI
I know this question is old, but here is an easier solution:
float bearing = loc1.bearingTo(loc2);
Try this for accurate result:
private static double degreeToRadians(double latLong) {
return (Math.PI * latLong / 180.0);
}
private static double radiansToDegree(double latLong) {
return (latLong * 180.0 / Math.PI);
}
public static double getBearing() {
//Source
JSONObject source = step.getJSONObject("start_location");
double lat1 = Double.parseDouble(source.getString("lat"));
double lng1 = Double.parseDouble(source.getString("lng"));
// destination
JSONObject destination = step.getJSONObject("end_location");
double lat2 = Double.parseDouble(destination.getString("lat"));
double lng2 = Double.parseDouble(destination.getString("lng"));
double fLat = degreeToRadians(lat1);
double fLong = degreeToRadians(lng1);
double tLat = degreeToRadians(lat2);
double tLong = degreeToRadians(lng2);
double dLon = (tLong - fLong);
double degree = radiansToDegree(Math.atan2(sin(dLon) * cos(tLat),
cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(dLon)));
if (degree >= 0) {
return degree;
} else {
return 360 + degree;
}
}
You can test bearing result on http://www.sunearthtools.com/tools/distance.php .
In the formula
float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);
the term (M_PI * .5f) means π/2 which is 90°. That means that it is the same formula that you stated at first, because regarding to the figure above it holds
β = arctan (a/b) = 90° - arctan(b/a).
So both formulas are similar if a refers to the difference in longitude and b in the difference in latitude. The last formula calculates again the same using the first part of my equation.
a in the diagram is the longitude difference, b is the latitude difference therefore in the method you have written you've got them the wrong way round.
a = destination.latitude - user.latitude; // should be b
b = destination.longitude - user.longitude; // should be a
Try switching them and see what happens.
See Palund's response for answers to the rest of your questions.
/*
Kirit vaghela answer has been modified..
Math.sin gives the radian value so to get degree value we need to pass Math.toRadians(value) inside Math.sin() or Math.cos()
*/
double lat1 = 39.099912;
double lat2 = 38.627089;
double lng1 = -94.581213;
double lng2 = -90.200203;
double dLon = (lng2-lng1);
double x = Math.sin(Math.toRadians(dLon)) * Math.cos(Math.toRadians(lat2));
double y = Math.cos(Math.toRadians(lat1))*Math.sin(Math.toRadians(lat2)) - Math.sin(Math.toRadians(lat1))*Math.cos(Math.toRadians(lat2))*Math.cos(Math.toRadians(dLon));
double bearing = Math.toDegrees((Math.atan2(x, y)));
System.out.println("BearingAngle : "+bearing);
If you want you can take a look at the code used in mixare augmented reality engine, it's on github and there's an iPhone version as well: github.com/mixare
inputs are in degrees.
#define PI 3.14159265358979323846
#define RADIO_TERRESTRE 6372797.56085
#define GRADOS_RADIANES PI / 180
#define RADIANES_GRADOS 180 / PI
double calculateBearing(double lon1, double lat1, double lon2, double lat2)
{
double longitude1 = lon1;
double longitude2 = lon2;
double latitude1 = lat1 * GRADOS_RADIANES;
double latitude2 = lat2 * GRADOS_RADIANES;
double longDiff= (longitude2-longitude1) * GRADOS_RADIANES;
double y= sin(longDiff) * cos(latitude2);
double x= cos(latitude1) * sin(latitude2) - sin(latitude1) * cos(latitude2) * cos(longDiff);
// std::cout <<__FILE__ << "." << __FUNCTION__ << " line:" << __LINE__ << " "
return fmod(((RADIANES_GRADOS *(atan2(y, x)))+360),360);
}

Following a straight line (via Path?)

I'm working on a game which will use projectiles. So I've made a Projectile class and a new instance is created when the user touches the screen:
#Override
public boolean onTouch(View v, MotionEvent e){
float touch_x = e.getX();
float touch_y = e.getY();
new Projectile(touch_x, touch_y);
}
And the Projectile class:
public class Projectile{
float target_x;
float target_y;
Path line;
public Projectile(float x, float y){
target_x = x;
target_y = y;
line = new Path();
line.moveTo(MyGame.mPlayerXPos, MyGame.mPlayerYPos);
line.lineTo(target_x, target_y);
}
}
So this makes a Path with 2 points, the player's position and and touch coords. My question is - How can you access points on this line? For example, if I wanted to get the x,y coords of the Projectile at the half point of the line, or the point the Projectile would be at after 100 ticks (moving at a speed of X pixels/tick)?
I also need the Projectile to continue moving after it reaches the final point.. do I need to use line.addPath(line) to keep extending the Path?
EDIT
I managed to get the Projectiles moving in a straight line, but they're going in strange directions. I had to fudge some code up:
private void moveProjectiles(){
ListIterator<Projectile> it = Registry.proj.listIterator();
while ( it.hasNext() ){
Projectile p = it.next();
p.TimeAlive++;
double dist = p.TimeAlive * p.Speed;
float dx = (float) (Math.cos(p.Angle) * dist);
float dy = (float) (Math.sin(p.Angle) * dist);
p.xPos += dx;
p.yPos += -dy;
}
}
The Angle must be the problem.. I'm using this method, which works perfectly:
private double getDegreesFromTouchEvent(float x, float y){
double delta_x = x - mCanvasWidth/2;
double delta_y = mCanvasHeight/2 - y;
double radians = Math.atan2(delta_y, delta_x);
return Math.toDegrees(radians);
}
However, it returns 0-180 for touches above the center of the screen, and 0 to -180 for touches below. Is this a problem?
The best way to model this is with parametric equations. No need to use trig functions.
class Path {
private final float x1,y1,x2,y2,distance;
public Path( float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.distance = Math.sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
public Point position( float t) {
return new Point( (1-t)*x1 + t*x2,
(1-t)*y1 + t*y2);
}
public Point position( float ticks, float speed) {
float t = ticks * speed / distance;
return position( t);
}
}
Path p = new Path(...);
// get halfway point
p.position( 0.5);
// get position after 100 ticks at 1.5 pixels per tick
p.position( 100, 1.5);
From geometry, if it's a straight line you can calculate any point on it by using polar coordinates.
If you find the angle of the line:
ang = arctan((target_y - player_y) / (target_x - player_x))
Then any point on the line can be found using trig:
x = cos(ang) * dist_along_line
y = sin(ang) * dist_along_line
If you wanted the midpoint, then you just take dist_along_line to be half the length of the line:
dist_along_line = line_length / 2 = (sqrt((target_y - player_y)^2 + (target_x - player_x)^2)) / 2
If you wanted to consider the point after 100 ticks, moving at a speed of X pixels / tick:
dist_along_line = 100 * X
Hopefully someone can comment on a way to do this more directly using the android libs.
First of all, the Path class is to be used for drawing, not for calculation of the projectile location.
So your Projectile class could have the following attributes:
float positionX;
float positionY;
float velocityX;
float velocityY;
The velocity is calculated from the targetX, targetY, playerX and playerY like so:
float distance = sqrt(pow(targetX - playerX, 2)+pow(targetY - playerY, 2))
velocityX = (targetX - playerX) * speed / distance;
velocityY = (targetY - playerY) * speed / distance;
Your position after 20 ticks is
x = positionX + 20 * velocityX;
y = positionY + 20 * velocityY;
The time it takes to reach terget is
ticksToTarget = distance / velocity;
Location of halp way point is
halfWayX = positionX + velocityX * (tickToTarget / 2);
halfWayY = positionY + velocityY * (tickToTarget / 2);

Categories

Resources