"I'm working with J2ME in eclipse, and need to use the method acos() belongs to the Math package, the question is that the package available for J2ME Math is not that function (limitations of mobiles), then I want an algorithm or method alternative to calculate the cosine.
I need the "acos" to calculate the following formula:
long2 = cood[i].getLongitud();
lat2 = cood[i].getLatitud();
dlong = (long1 - long2);
c = dlong * degtorad;
dvalue = (Math.sin(a) * Math.sin(b))+(Math.cos(a) * Math.cos(b)*Math.cos(c));
dd = Math.acos(dvalue) * radtodeg;
km = (dd * 111.302);
This function allows me from two geographical coordinates to calculate the distance in kilometers between them. If there is an alternative method to calculate this distance (where the cosine is not used), I also would be useful.
Any help on this?
public static double acos(double a)
{
final double epsilon=1.0E-7;
double x=a;
do {
x-=(Math.sin(x)-a)/Math.cos(x);
}
while (Math.abs(Math.sin(x)-a)>epsilon);
// returned angle is in radians
return -1*(x-Math.PI/2);
}
You could try to implement the arccosine as shown here.
If you have the Location API available you can use the distance method from the Coordinates class. From the Coordinates API:
public float distance(Coordinates to)
Calculates the geodetic distance between the two points
according to the ellipsoid model of WGS84.
Altitude is neglected from calculations.
The implementation shall calculate this as exactly as it can.
However, it is required that the result is within 0.35% of the correct result.
Related
I'm quite confused about distance calculations.
I'm using this website as a reference:
https://www.omnicalculator.com/other/latitude-longitude-distance
Then I got an implementation of a distance calculation (haversine) from here:
https://tutorialspoint.dev/algorithm/geometric-algorithms/program-distance-two-points-earth
And I want to compare this with various different implementations from Spatial4J.
I'm using these coordinates to test with:
Point 1: 40.688939, -74.04455
Point 2: 40.746853, -73.985633
And I'm getting pretty big differences between different ways of calculating the distance between these two points.
First, what mainly concerns me is that the website (omnicalculator) and the implementation from tutorialspoint agree completely on the distance: 8.132 km
But none of my Spatial4J calculations agree with that number. The one that comes closest to it is the CartesianDistCalc implementation at 8.262 km. The tutorialspoint demo code claims to be using haversine but the output of Spatial4J haversine DistCalc implementation is quite a ways off at 7.313 km.
But can somebody explain to me where these differences are coming from and what the "correct" one is?
Below is my experimental code:
import org.junit.jupiter.api.Test;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.CartesianDistCalc;
import org.locationtech.spatial4j.distance.GeodesicSphereDistCalc;
class GeodesicCalculationTest {
#Test
void testGeodesicCalculations(){
SpatialContext ctx = SpatialContext.GEO;
var startPoint = ctx.getShapeFactory().pointLatLon(40.688939, -74.04455);
var endPoint = ctx.getShapeFactory().pointLatLon(40.746853, -73.985633);
System.out.println("GEO spatial context: " + ctx.calcDistance(startPoint, endPoint) * 100);
System.out.println("Haversine: " + new GeodesicSphereDistCalc.Haversine().distance(startPoint, endPoint) * 100);
System.out.println("Law of cosine: " + new GeodesicSphereDistCalc.LawOfCosines().distance(startPoint, endPoint) * 100);
System.out.println("Vincenty: " + new GeodesicSphereDistCalc.Vincenty().distance(startPoint, endPoint) * 100);
System.out.println("Cartesian: " + new CartesianDistCalc().distance(startPoint, endPoint) * 100);
System.out.println("Tutorials Point (haversine): " + distance(startPoint.getLat(), endPoint.getLat(), startPoint.getLon(), endPoint.getLon()));
}
public static double distance(double lat1, double lat2, double lon1, double lon2) {
// The math module contains a function
// named toRadians which converts from
// degrees to radians.
lon1 = Math.toRadians(lon1);
lon2 = Math.toRadians(lon2);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
// Haversine formula
double dlon = lon2 - lon1;
double dlat = lat2 - lat1;
double a = Math.pow(Math.sin(dlat / 2), 2)
+ Math.cos(lat1) * Math.cos(lat2)
* Math.pow(Math.sin(dlon / 2),2);
double c = 2 * Math.asin(Math.sqrt(a));
// Radius of earth in kilometers. Use 3956
// for miles
double r = 6371;
// calculate the result
return(c * r);
}
}
And the output of running it:
GEO spatial context: 7.31307025220976
Haversine: 7.31307025220976
Law of cosine: 7.313070251733588
Vincenty: 7.3130702522095286
Cartesian: 8.261503667613857
Tutorials Point (haversine): 8.131763102409689
I'm multiplying the Spatial4J calculations by 100 which is also confusing to me... it doesn't really make sense that Spatial4J is giving me answers as 1/100th of a kilometer???
I realise I must be doing something wrong or completely misunderstanding some premises here. I would really appreciate some help in understanding what that is.
The result of SpatialContext.calcDistance(Point p, Point p2) is in degrees. To convert those degrees to km you need to multiply it by the constant DistanceUtils.DEG_TO_KM
SpatialContext ctx = SpatialContext.GEO;
var startPoint = ctx.getShapeFactory().pointLatLon(40.688939, -74.04455);
var endPoint = ctx.getShapeFactory().pointLatLon(40.746853, -73.985633);
double distanceInDegrees = ctx.calcDistance(startPoint, endPoint);
double distanceInKm = distanceInDegrees * DistanceUtils.DEG_TO_KM;
System.out.println("GEO spatial context: " + distanceInKm);
This gives you the output:
GEO spatial context: 8.131774297975046
Which is very close to your example from
Tutorials Point (haversine): 8.131763102409689
The difference is because Spatial4j is using the value 6371.0087714 for the radius of the earth in km, where as in your example you are using 6371.
I'm using firebase (android) to store data and I'm saving users like below:
users{
abcdefghi{
name:"abc",
lat:"12.988",
long:-0.123,
desc:"all other desc"
},KLMNGHT{
name:"def",
lat:"11.988",
long:-1.123,
desc:" other desc"
}
}
I want to display all users who comes into my radius zone(proximity zone) which are defined by me according to my location . I am out of ideas . I looked upon Haversine formula. But i don't know to achieve that.
What is the best algorithm to find user?
private const double EARTH_RADIUS = 6378.137;
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a/2),2) +
Math.Cos(radLat1)*Math.Cos(radLat2)*Math.Pow(Math.Sin(b/2),2)));
s = s * EARTH_RADIUS;
s = Math.Round(s * 10000) / 10000;
return s;
}
Maybe this code can help you.
So is the question strictly: given two points on earth A(lan1,lon1) B(lan2,lon2) find the distance between A and B? When you are saying 'best algorithm',do you mean best in terms of development time,time,memory?!
Anyways,assuming 'best' in terms of development time you could use:
1.Google's API explained here: https://developers.google.com/maps/documentation/distance-matrix/intro#DistanceMatrixRequests
2.Here is a simple implementation- but I haven't tested it myself yet-
https://www.geodatasource.com/developers/java
In my app I use the method "DistanceTo()" to get the distance between to places, but don't works correctly!! This is my code for this distance:
double latitudine;
double longitudine;
latitudine = 40.17;
longitudine = 24.9;
Location qui = new Location("Corrente");
qui.setLatitude(latitudine);
qui.setLatitude(longitudine);
double latitudine2;
double longitudine2;
latitudine2 = 40.16;
longitudine2 = 25;
Location due = new Location("Corrente2");
due.setLatitude(latitudine2);
due.setLatitude(longitudine2);
float b = qui.distanceTo(due);
Toast.makeText(getBaseContext(), "DESTINAZIONE KILOMETRI b:" + b, Toast.LENGTH_SHORT).show();
The toast show to me 11077.14 meters, but the distance should be 8.5 km!! as shown in these sites: http://www.movable-type.co.uk/scripts/latlong.html http://www.mapdevelopers.com/distance_from_to.php
In this case the difference is for about 3km, but sometimes the difference is sometimes the difference is even more!! Please help me! Why this code doesn't work correctly? Thanks
you are not calling setLongitude on both Locations, you need:
qui.setLongitude(longitudine);
due.setLongitude(longitudine2);
Might want to take a look at Point2D.Distance instead. It returns
the distance between the two sets of specified coordinates.
While integrating google API in Java, I need to calculate another place's lat/long using existing lat/long, distance and direction. How to calculate that point?
The following PHP code provides the point from $lat1,$lng1 a distance $dist (kms) along a bearing $brng.
function destination($lat1,$lng1,$dist,$brng){
$alpha = $dist/6371; // km
$lat2 = asin((sin($lat1)*cos($alpha)) +(cos($lat1)*sin($alpha)*cos($brng)) );
$lng2 = $lng1 + atan2(sin($brng)*sin($alpha)*cos($lat1),cos($alpha)-sin($lat1)*sin($lat2));
return array(toDeg($lat2),toDeg($lng2));
}
$lat1,$lng1 & $brng in radians
function toRad($deg) {
// Converts numeric degrees to radians
return $deg * pi() / 180;
}
function toDeg($rad){
return $rad * 180 / pi();
}
Let's say that I am collecting users location data every 20 minutes. At the end of the day I have an ArrayList of objects that contain lat and long fields and other data.
I have two problems that I am facing and trying to figure out:
Some of the locations are taken from inside of a building so they are not very accurate and could be spread around the actual location where the user was at the time.
Some of the locations are taken at different times but from the same location, as the user didn't moved.
What I want to achieve is to find all the locations that are near one another: lets say 70 meters, find the average location of all those locations and replace them only with this one average location.
So I am coming to the two important questions:
What would be the best way to find all near locations < 70 meter distance (Take in mind that the array contains valid changes in location. So I have to find the groups of near ones and leave the others intact).
Is there a method or a way to find the average location of many near ones?
Regarding near positions I previously answered a similar question here: Android Maps v2 - animate camera to include most markers
Specifically I think you would be able to use this piece of code:
private List<Marker> getSurroundingMarkers(List<Marker> markers,
LatLng origin, int maxDistanceMeters) {
List<Marker> surroundingMarkers = surroundingMarkers = new ArrayList<Marker>();
if (markers == null) return surroundingMarkers ;
for (Marker marker : markers) {
double dist = distBetween(origin, marker.getPosition());
if (dist < maxDistanceMeters) {
surroundingMarkers.add(marker);
}
}
return surroundingMarkers;
}
private float distBetween(LatLng pos1, LatLng pos2) {
return distBetween(pos1.latitude, pos1.longitude, pos2.latitude,
pos2.longitude);
}
/** distance in meters **/
private float distBetween(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 3958.75;
double dLat = Math.toRadians(lat2 - lat1);
double dLng = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2)
* Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double dist = earthRadius * c;
int meterConversion = 1609;
return (float) (dist * meterConversion);
}
Possibly rewriting the Marker part to use LatLng instead.
regarding the averaging, it should be a simple matter of (say you have n LatLng's):
lat_avg = (lat1+lat2+...+latn)/n
lng_avg = (lng1+lng2+...+lngn)/n
latlng_avg = new LatLng(lat_avg, lat_lng)
I' not sure how you're getting the user's location - whether your using a traditional LocationManager or play services. I've always modeled my location getting on this approach documented on the android developers blog. If you're flexible in switching between location gathering methods, whether the person is inside or outside should not matter that much. You should be getting the best possible location at any given time.
Assuming you're getting locations on a sufficient time schedule (I don't know when you're checking for updates but since you want everything inside a 70m radius I'm assuming its on a time schedule and not distance change) the basic way to find the average point is:
(1) Convert each lat/long pair into a unit-length 3D vector.
(2) Sum each of those vectors
(3) Normalise the resulting vector
(4) Convert back to spherical coordinates
That approach is documented here as well as in a much earlier SO post on calculating the average of a set of angles
The example code is pretty easy to follow - just plug in the lat long values you get from your location grab and you should be ok.
Well for markers that come from the same location I have created the following method:
public ArrayList<MyLocation> removeSameLocationMarkers(List<ParseObject> objects, int maxDistanceMeters)
{
boolean isLocationExist;
ArrayList<MyLocation> acceptedLocations = new ArrayList<MyLocation>();
if (objects == null) return acceptedLocations;
for (ParseObject location1 : objects)
{
isLocationExist = false;
for (MyLocation location2 : acceptedLocations)
{
if (!location1.equals(location2))
{
float distance = distBetween(location1.getDouble("latitude"), location1.getDouble("longitude"), location2.getLatitude(), location2.getLongitude());
if (distance < maxDistanceMeters)
{
location2.addTimeToLocation(location1.getString("time"));
isLocationExist = true;
}
}
}
if (!isLocationExist)
{
Location newLocation = new Location("");
newLocation.setLatitude(location1.getDouble("latitude"));
newLocation.setLongitude(location1.getDouble("longitude"));
String provider = location1.getString("provider");
if (provider != null)
{
newLocation.setProvider(provider);
}
MyLocation newMyLocation = new MyLocation(newLocation);
newMyLocation.addTimeToLocation(location1.getString("time"));
acceptedLocations.add(newMyLocation);
}
}
return acceptedLocations;
}