I know location1.distanceTo(location2)
I need to calculate
distanceX
and
distanceY
from two locations. Is there a way to do it?
CORRECT ANSWER based on njzk2 answer
private Point getDistanceXY(Location location1, Location location2) {
int angle = (int) location1.bearingTo(location2);
Location interm = new Location(location1);
interm.setLongitude(location2.getLongitude());
int distanceX = (int) location1.distanceTo(interm);
int distanceY = (int) location2.distanceTo(interm);
if (angle<=-90) distanceX=-distanceX;
else if (angle>-90 && angle<0) {
distanceX=-distanceX;
distanceY=-distanceY;
}
Log.e("distXY "+name, distanceX+" "+distanceY+" "+angle);
return new Point(distanceX, distanceY);
}
What you could do is create a middle point :
Location interm = new Location(location1);
iterm.setLongitude(location2.getLongitude());
And then,
double distanceX = location1.distance(interm);
double distanceY = interm.distance(location2);
You can use bearingTo() in combination with distanceTo() and a little bit of trigonometry to calculate the X and Y components.
double distY = distance * sin(angle);
double distX = distance * cos(angle);
Related
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.
I have two overlays in my openstreetmap app, Itemized overlay and Path overlay together. I want to provide click inside the path inside the path overlay and custom marker
Not available with PathOverlay.
Use OSMBonusPack Polyline.
If you just want to open a bubble, use setInfoWindow.
If you want to do something else, inherit from Polyline class, override onSingleTapConfirmed, and use isCloseTo.
Finally I got answer for the Above question...
Add all path overlays into a single layer.
In on single tap check isPointOnLine(lox,loy,ltx,lty, x, y)
public boolean isPointOnLine(double lox, double loy, double ltx,
double lty, double x, double y) {
// determine if point is on line
Double dx = x - lox;
Double dy = y - loy;
Double tx = ltx - lox;
Double ty = lty - loy;
// normalise the line vector
Double t1 = new Double(1 / Math.sqrt(tx * tx + ty * ty));
tx *= t1;
ty *= t1;
// calculate inverse length of secondary vector
Double dl = new Double(1 / Math.sqrt(dx * dx + dy * dy));
// take dot product of normalised line vector, and rotated normalised
// secondary vector
Double dot = (dy * tx - dx * ty) * dl;
// Increase these values for less or more picky
if (dot < -0.2 || dot > 0.2)
return false;
// calculate distance along line segment by taking dot product of
// normalised
// line vector and un-normalised secondary vector
Double dis = tx * dx + ty * dy;
if (dis < 0 || dis > 1 / t1)
return false;
return true;
}
I am trying to create a pad-like view in android. I got a circle that follows user's gestures and I am using distance to keep the circle of going outside the main circle of the pad control.
My problem is I want the circle to keep following the gesture, but to stay inside of the main circle. I am using the formula for finding a point using angle and radius, but it does some funky stuff.
I am translating the canvas, so that the center of the main circle is at 0, 0.
Here is the code:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(this.mainRadius, this.mainRadius);
canvas.drawCircle(0, 0, this.mainRadius, this.debugPaint);
canvas.drawCircle(this.handleX, this.handleY, this.handleRadius, this.handlePaint);
}
private void translateHandle(MotionEvent event) {
int x = (int) (event.getX() - this.mainRadius);
int y = (int) (event.getY() - this.mainRadius);
double distance = distanceFromCenter(x, y);
if (distance <= this.maxDistance) {
this.handleX = x;
this.handleY = y;
} else {
float angle = (float) Math.toDegrees(Math.atan2(y, x));
if (angle < 0)
angle += 360;
this.handleX = (int) ((this.mainRadius - this.handleRadius) * Math.cos(angle));
this.handleY = (int) ((this.mainRadius - this.handleRadius) * Math.sin(angle));
}
//onTranslateHandle(distance);
}
And here is the funky stuff in a gif image:
I cannot verify this change into your code snippet but do hope it gives some idea how to proceed further anyway;
private void translateHandle(MotionEvent event) {
float x = event.getX() - this.mainRadius;
float y = event.getY() - this.mainRadius;
double distance = distanceFromCenter(x, y);
if (distance > this.maxDistance) {
// If distance is i.e 2.0 and maxDistance is 1.0 ==> adjust is 0.5
// which repositions x and y making distance 1.0 maintaining direction
double adjust = this.maxDistance / distance;
x = (float)(x * adjust);
y = (float)(y * adjust);
}
this.handleX = (int)x;
this.handleY = (int)y;
}
I can update the answer where needed if this does not give any useful results.
I've been researching this on Google and SO but I'm stuck, I think I'm missing something fundamental. Most examples I've seen don't deal with an arbitrary mapWidth and a single point, just the span of an Overlay.
I have a database of map points, a MapView and a Geocoder. I can search for a postcode in my app, and have an Address returned by my Geocoder.
Using this Address, I can build a GeoPoint and search my DB and get back a list of nearby points. The issue comes from trying to zoomToSpan using a span constructed from from the returned Address point and the distance to the nearest point in the database.
I only want the span to encompass the nearest two points (if available). Here's the relevant code:
Collections.sort(listingDisplay, mComparator);
listingDisplayAdapter.notifyDataSetChanged();
float spanWidth =0;
if (listingDisplay.size() > 1) {
spanWidth = (float) (2 * distanceFromPoint(listingDisplay.get(1),
current));
} else if (listingDisplay.size() == 1) {
spanWidth = (float) (2 * distanceFromPoint(listingDisplay.get(0),
current));
}
Log.v(TAG, "SpanWidth: " + spanWidth);
// Create span
int minLat = (int) (current.getLatitudeE6() - (spanWidth * 1E6) / 2);
int maxLat = (int) (current.getLatitudeE6() + (spanWidth * 1E6) / 2);
int minLong = (int) (current.getLongitudeE6() - (spanWidth * 1E6) / 2);
int maxLong = (int) (current.getLongitudeE6() + (spanWidth * 1E6) / 2);
// Zoom against span. This appears to create a very small region that doesn't encompass the points
mapController.setCenter(current);
mapController.zoomToSpan(Math.abs( minLat - maxLat ), Math.abs( minLong - maxLong ));
ListingDisplay contains a list of the closest points, with a comparator, mComparator sorting this list with the closest locations to my returned Address (the GeoPoint called: current) at the top of the list .
I then set the value of spanWidth based on the closest, and try to figure out the span from this.
My question is, how can I construct a span from a given distance and centerpoint?
After a very, very long time, I eventually realized that I wasn't considering some important information, chiefly among them was the fact that distances on Android are calculated using the WGS84 ellipsoid.
I ended up using the helper methods inside Jan Matuschek's excellent and simple GeoLocation class, which comes with a very thorough explanation of the concepts involved.
My method essentially boiled down to the following. It can probably be optimized a good deal, down to a simple SQL query, but here it is for my purposes, where listingDisplay is an array of database-retrieved custom LocationNode objects, and the GeoPoint current is created directly from a returned Address of a standard Android Geocoder.
public void setRegionForGeoPoint(GeoPoint current) {
// Earth radius in KM
final double EARTH_RADIUS = 6371.01;
// Dummy span distance in KM for initial search; distance buffer is in M
final double DISTANCE_BUFFER = 50;
final double dummyDistance = 100.0;
//Create a list to store distances
List<Double> distancesList = new ArrayList<Double>();
// Loop through and modify LocationNodes with distance from user
for (LocationNode location : listingDisplay) {
location.setDistance((float) distanceFromUser(location));
// Dynamically calculate distance from our current point (epicentre)
distancesList.add(distanceFromPoint(location, current));
}
// Sort distances
Collections.sort(distancesList);
// Calculate regional span
float spanWidth = (float) dummyDistance;
double distance = 0;
if (distancesList.size() > 0) {
if (distancesList.size() > 1) {
distance = distancesList.get(1);
spanWidth = (float) (distance + DISTANCE_BUFFER);
} else if (distancesList.size() == 1) {
distance = distancesList.get(0);
spanWidth = (float) (distance + DISTANCE_BUFFER);
}
//Obtain the spanwidth in metres.
double spanWidthInKM = (double) spanWidth / 1000;
// Create span
GeoLocation[] boundingBoxSpan = currentGeoLocation
.boundingCoordinates(spanWidthInKM, EARTH_RADIUS);
//Create min/max values for final span calculation
int minLatSpan = (int) (boundingBoxSpan[0].getLatitudeInDegrees() * 1E6);
int maxLatSpan = (int) (boundingBoxSpan[1].getLatitudeInDegrees() * 1E6);
int minLongSpan = (int) (boundingBoxSpan[0].getLongitudeInDegrees() * 1E6);
int maxLongSpan = (int) (boundingBoxSpan[1].getLongitudeInDegrees() * 1E6);
//Finally calculate span
int latSpanE6 = Math.abs(minLatSpan - maxLatSpan);
int lonSpanE6 = Math.abs(minLongSpan - maxLongSpan);
// Set center
mapController.setCenter(current);
// Zoom to span
mapController.zoomToSpan(latSpanE6, lonSpanE6);
} else {
//TODO: Handle the case when we have no distance values to use
}
}
public double distanceFromPoint(LocationNode location, GeoPoint point) {
// Calculate distance from user via result
Location locationA = new Location("point A");
locationA.setLatitude(location.getLatitude());
locationA.setLongitude(location.getLongitude());
Location locationB = new Location("point B");
locationB.setLatitude((double) (point.getLatitudeE6() / 1E6));
locationB.setLongitude((double) (point.getLongitudeE6() / 1E6));
double distance = locationA.distanceTo(locationB);
Log.v(TAG, "Calculated Distance: " + distance);
return distance;
}
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);