Get location from a distance of a geographic point - java

I am working on an Android app and I have latitude and longitude of a geographic point and I would like to get coordinates of the place located from X meters (North) and Y meters (West). GeoPoint does not implement this kind of functionality so I have to find a way to do it by myself...
I made some searches but I did not find anything interesting, do you have any idea how to proceed?

Tricky, because the distance between two whole longitudes depends on the latitude, and the distance also depends on the altitude...
To make things simple, I'd try and guess the longitude and latitude, and check the distance between the guess and the origin point. This is easier because getting the distance between two points has already been done.
Start with one axis, and do a kind of binary search until you find the location X meters to the north. Then do the same for the other axis.

With the following method you will get the latitude and longitude to add to the original location to get to the final one. Have in mind that this only works over relatively small distances, as it is ignoring the earth curvature.
private static final float GAP_LAT_LON = 0.00005f; //aprox 5 meters
private float results[] = new float[1];
private PointF getGaps(Location initial, float distanceX, float distanceY, PointF gaps){
Location.distanceBetween(initial.getLatitude(), initial.getLongitude(), initial.getLatitude(), initial.getLongitude() + GAP_LAT_LON, results);
double factorX = GAP_LAT_LON / results[0];
Location.distanceBetween(initial.getLatitude(), initial.getLongitude(), initial.getLatitude() + GAP_LAT_LON, initial.getLongitude(), results);
double factorY = GAP_LAT_LON / results[0];
if(gaps == null)
gaps = new PointF();
gaps.set((float)(distanceX * factorX), (float)(distanceY * factorY));
return gaps;
}
//to use
private void teste(){
PointF gaps = null;
Location initial = new Location("");
initial.setLatitude(23.5);
initial.setLongitude(13.2);
//100 meters West/East and 300 meters North/South
getGaps(initial, 100, 300, gaps);
//gaps.x returns x offset to add/subtract to initial latitude
//gaps.y returns y offset to add/subtract to initial longitude
}
good luck

Related

Need a way in JTS library to find new Lat Long coordinates at X length in given angle from starting lat long

I am trying my hands on locationtech JTS library. I wanted to find any built methods from JTS that can help me get a new coordinate given that I have following data with me:
Originating Point
Distance to the new coordinate
Bearing angle direction for finding the new coordinate
Also are there any resources apart from Javadocs of JTS library for easier comprehension?
To do that you simply set the start point, distance and direction in the GeodeticCalculator.
DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
GeodeticCalculator calc = new GeodeticCalculator(crs);
GeometryFactory geomFactory = new GeometryFactory();
Point point = geomFactory.createPoint(new Coordinate(0.0, 50.0));
calc.setStartingGeographicPoint(point.getX(), point.getY());
// azimuth in degrees -180 - 180
double azimuth = 90.0;
// distance in metres
double distance = 50;
calc.setDirection(azimuth, distance);
Point2D p = calc.getDestinationGeographicPoint();
System.out.println(p);

X Y distance from longitude and latitude

I have two set's of longitude and latitude, i am desperately trying to figure out how many meters point A is displaced from point B, horizontally and vertically.
My goal would be have to +/-X and +/-Y values - I already have the shortest distance between the two points via Location.distanceBetween()....i thought i could use this with the Location.bearingTo() to find the values im looking for via basic trigonometry.
My thinking was i could use the bearing as angle A, 90 degrees as angle C and legnth of Side C (distanceBetween) to calculate the legnth of side A (x axis) and B (y axis) but the results were underwhelming to say the least lol
//CALCULATE ANGLES
double ANGLE_A;
ANGLE_A = current_Bearing; //Location.bearingTo()
ANGLE_A = ANGLE_A*Math.PI/180; //CONVERT DEGREES TO RADIANS
double ANGLE_C;
ANGLE_C = 90; // Always Right Angle
ANGLE_C = ANGLE_C*Math.PI/180; //CONVERT DEGREES TO RADIANS
double ANGLE_B;
ANGLE_B = 180 - ANGLE_A - ANGLE_C; // 3 sides of triangle must add up to 180, if 2 sides known 3rd can be calced
ANGLE_B = ANGLE_B*Math.PI/180; //CONVERT DEGREES TO RADIANS
//CALCULATE DISTANCES
double SIDE_C = calculatedDistance; //Location.distanceTo()
double SIDE_A = Math.sin(ANGLE_A) * SIDE_C /Math.sin(ANGLE_C);
double SIDE_B = Math.sin(ANGLE_B)*SIDE_C/Math.sin(ANGLE_C);
What im noticing is that my bearing changes very little between the two points regardless of how we move, though mind you im testing this at 10 - 100m distance, its always at 64.xxxxxxx and only the last few decimals really change.
All the online references i can find always look at computing the shortest path, and although this awesome site references x and y positions it always ends up combining them into shortest distance again
Would SUPER appreciate any pointers in the right direction!
Since the earth is not flat, your idea with 90 degree angles will not work properly.
What might be better, is this.
Lets say your 2 known points A and B have latitude and longitude latA, longA and latB, longB.
Now you could introduce two additional points C and D with latC = latA, longC = longB, and latD = latB, longD = longA, so the points A, B, C, D form a rectangle on the earth's surface.
Now you can simply use distanceBetween(A, C) and distanceBerween(A, D) to get the required distances.
It may be possible to utilize Location.distanceBetween(), if following conditions meet,
the points are located far apart from polar regions and
distance is short enough (compared to radius of the Earth).
The way is very simple. Just fix either longitude or latitude and vary only the other. Then calculate distance.
Location location1 = new Location("");
Location location2 = new Location("");
location1.setLatitude(37.4184359437);
location1.setLongitude(-122.088038921);
location2.setLatitude(37.3800232707);
location2.setLongitude(-122.073230422);
float[] distance = new float[3];
Location.distanceBetween(
location1.getLatitude(), location1.getLongitude(),
location2.getLatitude(), location2.getLongitude(),
distance
);
double lat_mid = (location1.getLatitude() + location2.getLatitude()) * 0.5;
double long_mid = (location1.getLongitude() + location2.getLongitude()) * 0.5;
float[] distanceLat = new float[3];
Location.distanceBetween(
location1.getLatitude(), long_mid,
location2.getLatitude(), long_mid,
distanceLat
);
float[] distanceLong = new float[3];
Location.distanceBetween(
lat_mid, location1.getLongitude(),
lat_mid, location2.getLongitude(),
distanceLong
);
double distance_approx = Math.sqrt(
Math.pow(distanceLong[0], 2.0) + Math.pow(distanceLat[0], 2.0)
);
Compare distance[0] and distance_approx, check whether accuracy meets your requiement.
If your points are close enough, you may easily calculate x-y distances from latitude / longitude once you know that 1 degree of latitude is 111km, and one degree of longitude is 111km * cos(latitude):
y_dist = abs(a.lat - b.lat) * 111000;
x_dist = abs(a.lon - b.lon) * 111000 * cos(a.lat);
For short distances we could easily ignore that earth is not exactly a sphere, the error is approximately 0.1-0.2% depending on your exact location.
There is no valid answer to this question until you define what projection.
The azimuth of a "straight" line varies along the route unless you are travelling exactly due south or due north. You can only calculate the angles at each node, or azimuth at a specific point along the route. Angles at the nodes will not add up to 180° because you're referring to an ellipsoidal triangle, and calculating an ellipsoidal triangle is a multiple-step process that in all honesty, is better left to the libraries out there such as OSGEO.
If you want to fit the geometry to a plane Cartesian, it is usually using the Lambert projection for areas mostly long on east and west directions, and Transverse Mercator on longer north to south projections. The entire Earth is mapped in the UTM (Universal Transverse Mercator) that will give you Cartesian coordinates anywhere, but in no case will you get perfect Eucldian geometry when dealing with geodetics. For instance, if you go south 10 miles, turn left 90° and go east for 10 miles, turn left 90° again, you can be anywhere from 10 miles from your starting point, to exactly back to where you started, if that point happened to be the North pole. So you may have a mathematically beautiful bearing on the UTM coordinate plane, but on the ground, you cannot turn the same angles as the UTM geometry indicates and follow that same path on ground. You will either follow a straight line on the ground and a curved line on a cartesian plane, or vice-versa.
You could do a distance between two points on the same northings and separately, the same eastings, and derive a north distance and an east distance. However, in reality the angles of this triangle will make sense only on paper, and not on the globe. If a plane took off at the bearing calculated by such a triangle, it would arrive in the wrong continent.

Get accurate lat/long given current point, distance and bearing

Given an existing point in lat/long, distance(inches) and bearing (in degrees converted to radians), I'm trying to calculate the new lat/long. But the resulting is some times inaccurate because I'm trying to measure a small area for the new lat/long. Currently, I'm trying to get the next foot lat/long but some times it has a 1-inch error. I used the following algorithm for this implementation. Referred this thread for the algorithm Get lat/long given current point, distance and bearing.
public LatLng calculateLatLangFromDistance(double brng, double distance, LatLng latLng){
double lat1 = Math.toRadians(latLng.getLatitude());
double long1 = Math.toRadians(latLng.getLongitude());
double br = Math.toRadians(brng);
double lat2 = Math.asin(Math.sin(lat1)*Math.cos(distance/r) +
Math.cos(lat1)* Math.sin(distance/r)* Math.cos(br));
double long2 = long1 + Math.atan2(Math.sin(br)*Math.sin(distance/r)*Math.cos(lat1),
Math.cos(distance/r)-Math.sin(lat1)*Math.sin(lat2));
lat2 = Math.toDegrees(lat2);
long2 = Math.toDegrees(long2);
LatLng latLng1 = new LatLng();
latLng1.setLongitude(long2);
latLng1.setLatitude(lat2);
return latLng1;
}
r is defined as 250826771.6535433 // //Radius of the Earth from inches
I'm using the above implementation to generate a 1-foot grid to mapbox map layer but resulting grid's some lines are 1-inch inaccurate. Is there any way to improve this implementation for better results or is there another way to get accurate lat/long for this?
this is the generated grid and I did a line drawing to get the idea about line length of the some lines in the grid.
You can use the spherical geometry utilities of the Google Maps SDK for Android utility library.
The specific method that you can use will be the computeOffset method. You can see it when you search the SphericalUtil in this link.
ComputeOffset returns the LatLng resulting from moving a distance from an origin in the specified heading (expressed in degrees clockwise from north). Here are the parameters that you need to specify:
from - The LatLng from which to start.
distance - The distance to travel.
heading - The heading in degrees clockwise from north.
To use this, add the utility library to your dependencies:
implementation 'com.google.maps.android:android-maps-utils:0.5'
This is the sample of a line of code that I use:
LatLng newCoordinates = SphericalUtil.computeOffset(new LatLng(-25.363, 131.044),30000, 90);
Hope this helps!

Converting image X,Y coordinates to longitude and latitude?

I have set values of minimum longitude and latitude of a specific static map image. That map image is a cut of some country.
/**
* Maximum longitude value of the map
*/
private float mapLongitudeMax;
/**
* Minimum longitude value of the map
*/
private float mapLongitudeMin;
/**
* Maximum latitude value of the map
*/
private float mapLatitudeMax;
/**
* Minimum latitude value of the map
*/
private float mapLatitudeMin;
And I have a BufferedImage called mapImage.
I have a method that I wrote with a friend that receives longitude and latitude and gives you an X and a Y position approximately on the map so you can draw something on the map.
Now if I want to move my mouse around the map, I want it to show longitude/latitude of my mouse position, that means I need to create a method which converts X and Y of the mouse position to longitude and latitude, which should do the opposite of my other method.
This is my method to convert globe coordinates to image X and Y:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr #oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
Now I tried thinking, the first thought that came into my head is just doing the same in reverse... so I started doing it and I ran into problems like, I can't get the value of adjLon or adjLat without having the longitude or latitude, so this can't be simply done by reversing it. I am all new to coordinates systems so it's all a bit confusing for me but I am starting to catch it up.
Any tips for me?
EDIT (Its not possible?)
According to this answer, you can't really get real results because the earth is not flat, it can't really be converted to a flat map with longitude and latitude without implementing a real mathematical algorithm to make it work with the changes.
There are few reasons in my code why the answer can not be exact:
Because of the reason above
Because my X,Y values are integers and not floats.
So my question now, if it is really impossible with my method?
Sadly, there's not an easy answer to this. While you can write the projection routines yourself, the easiest thing to do is probably to get a GIS library, but since I ended up doing this in C# and not Java, I don't know what's available.
The biggest piece of information you need is exactly which projection your map image uses. The Mercator Projection is quite popular, but it's not the only one. You also need to make sure that your chosen projection works for the range of latitudes and longitudes you want. The Mercator projection kind of breaks if you start going above +-70 N, so if you're doing a lot of positions at the poles that might not be the projection for you.
From what i read in your code, your image is in longitude/latitude coordinates, and you draw it on a canvas to be display on screen. Then you add your listener on this image is that correct ?
if this is correct the response is trival, as you can retrieve the X/Y position in your image via MouseListener method on Canvas and transform it base on the position of the mouse inside the canvas (methode getX/getY from mouseEvent) and the current dimension of the canvas and then translate this position within the bound of longitude/latitude.
longitude = minLongitude + (MouseEvent.getX/Canvas.Width)*maxLongitude
lalitude = minLaltitude + (MouseEvent.getY/Canvas.Height)*maxLatitude
If not then you will have to know as #ginkner say the projection technique use to pass from long/lat to X/Y and take the inverse transformation knowing that you will lost some information.
There is a difference between geography coordinate and geometry coordinate it is quite like the 3D earch surface and a canvas to draw on. The web Mercator projection or other popular projection coordinate systems are used for an abstraction of visualization of the earth surface. So that pixel shift in different location would result in different distances.
If you are looking for a some basic GIS java library to handle this type of problem, Geotools in Java could be one option. GeoTools

Check if a latitude and longitude is within a circle

See this illustration:
What I would like to know is:
How to create an area (circle) when given a latitude and longitude and the distance (10 kilometers)
How to check (calculate) if a latitude and longitude is either inside or outside the area
I would prefer if you can give me code example in Java or specifically for Android with Google Maps API V2
What you basically need, is the distance between two points on the map:
float[] results = new float[1];
Location.distanceBetween(centerLatitude, centerLongitude, testLatitude, testLongitude, results);
float distanceInMeters = results[0];
boolean isWithin10km = distanceInMeters < 10000;
If you have already Location objects:
Location center;
Location test;
float distanceInMeters = center.distanceTo(test);
boolean isWithin10km = distanceInMeters < 10000;
Here is the interesting part of the API used:
https://developer.android.com/reference/android/location/Location.html
Check this:
private boolean isMarkerOutsideCircle(LatLng centerLatLng, LatLng draggedLatLng, double radius) {
float[] distances = new float[1];
Location.distanceBetween(centerLatLng.latitude,
centerLatLng.longitude,
draggedLatLng.latitude,
draggedLatLng.longitude, distances);
return radius < distances[0];
}
Have you gone through the new GeoFencing API. It should help you. Normal implementation takes a lot of time. This should help you implementing it easily.
see https://developer.android.com/reference/android/location/Location.html
Location areaOfIinterest = new Location;
Location currentPosition = new Location;
areaOfIinterest.setLatitude(aoiLat);
areaOfIinterest.setLongitude(aoiLong);
currentPosition.setLatitude(myLat);
currentPosition.setLongitude(myLong);
float dist = areaOfIinterest.distanceTo(currentPosition);
return (dist < 10000);
Location didn't work for me, here's what I did.
import 'dart:math' show cos, sqrt, asin;
double calculateDistanceBetweenTwoLatLongsInKm(
double lat1, double lon1, double lat2, double lon2) {
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 -
c((lat2 - lat1) * p) / 2 +
c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2;
return 12742 * asin(sqrt(a));
}
Then just check if that distance is more (outside) or less (inside) than your radius in KM
Just in case if anyone is using GoogleMap and tried to apply location range by long&lat. You may try google.maps.Circle()
i.e. (mkr is your marker)
let yourcircle = new google.maps.Circle({
strokeColor: "#0079C3",
strokeOpacity: .8,
strokeWeight: 2,
fillColor: "#0079C3",
fillOpacity: 0.2,
map: this.map,
center: {'lat':your latitude, 'lng':your longitude},
radius: Math.sqrt(your distance range) * 1000,
});
Kotlin version
Using instance method distanceTo, something like this in a form of an extension function:
fun Location.checkIsInBound(radius: Double,center:Location):Boolean
= this.distanceTo(center)<radius
Usage example:
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
val isWithin2km = location?.checkIsInBound(2000.0,center) }
Besides using Location API's distanceTo or distanceBetween as stated in the above answers, You could also use Google Map Utility Library's SphericalUtil's computeDistanceBetween as well, however it takes LatLng as input.
If you mean by "How to create an area", that you want to draw the area on the map, you will find an example right in the map V2 reference doc for the class Circle.
For checking whether the distance between the center of the circle and your point is greater than 10 km I would suggest to use the static method Location.distanceBetween(...) as it avoids unnecessary object creations.
See also here (at the very end of the answer) for a code example in case the area is a polygon rather than a circle.

Categories

Resources