Calculate large distance between two points using GeoTools - gis

New to GeoTools and GIS and I am trying to calculate distance between Mumbai and Durban using GeoTools library. I am getting close to accurate results for small distances but when i go for bigger ones,the calculation is way too offcourse by 2000 km, i dont completely understand the CRS system .Below is my Code to calculate the distance between Mumbai and Durban
Coordinate source = new Coordinate(19.0760, 72.8777); ///Mumbai Lat Long
Coordinate destination1 = new Coordinate(-29.883333, 31.049999); //Durban Lat Long
GeometryFactory geometryFactory = new GeometryFactory();
Geometry point1 = geometryFactory.createPoint(source);
Geometry point2 = geometryFactory.createPoint(destination1);
CoordinateReferenceSystem auto = auto = CRS.decode("AUTO:42001,13.45,52.3");
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
Geometry g3 = JTS.transform(point1, transform);
Geometry g4 = JTS.transform(point2, transform);
double distance = g3.distance(g4);

This is what happens when you copy code blindly from stackexchange questions without reading the question it was based on which explains why.
All the times I've answered that question (and posted code like that) the questioner is trying to use lat/lon coordinates in degrees to measure a short distance in metres. The trick shown in your question creates an automatic UTM projection centred on the position specified after the "AUTO:42001," bit (in your case 52N 13E) - this needs to be the centre of the area you are interested in, so in your case those values are probably wrong anyway.
But you aren't interested in a small region Mumbai to Durban is a significant way around the Earth so you need to allow for the curvature of the Earth's surface. Also you aren't trying to do something difficult for which JTS is the only source of process (e.g buffering). In this case you should use the GeodeticCalculator which takes the shape of the Earth into account using the library from C. F. F. Karney, Algorithms for geodesics, J. Geodesy 87, 43–55 (2013).
Anyway enough explanation that no one will read in the future, here's the code:
public static void main(String[] args) {
DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
if (args.length != 4) {
System.err.println("Need 4 numbers lat_1 lon_1 lat_2 lon_2");
return;
}
GeometryFactory geomFactory = new GeometryFactory();
Point[] points = new Point[2];
for (int i = 0, k = 0; i < 2; i++, k += 2) {
double x = Double.valueOf(args[k]);
double y = Double.valueOf(args[k + 1]);
if (CRS.getAxisOrder(crs).equals(AxisOrder.NORTH_EAST)) {
System.out.println("working with a lat/lon crs");
points[i] = geomFactory.createPoint(new Coordinate(x, y));
} else {
System.out.println("working with a lon/lat crs");
points[i] = geomFactory.createPoint(new Coordinate(y, x));
}
}
double distance = 0.0;
GeodeticCalculator calc = new GeodeticCalculator(crs);
calc.setStartingGeographicPoint(points[0].getX(), points[0].getY());
calc.setDestinationGeographicPoint(points[1].getX(), points[1].getY());
distance = calc.getOrthodromicDistance();
double bearing = calc.getAzimuth();
Quantity<Length> dist = Quantities.getQuantity(distance, SI.METRE);
System.out.println(dist.to(MetricPrefix.KILO(SI.METRE)).getValue() + " Km");
System.out.println(dist.to(USCustomary.MILE).getValue() + " miles");
System.out.println("Bearing " + bearing + " degrees");
}
Giving:
working with a lon/lat crs
POINT (72.8777 19.076)
POINT (31.049999 -29.883333)
7032.866960793305 Km
4370.020928274692 miles
Bearing -139.53428618565218 degrees

Related

geotools GeodeticCalculator

Having two points and a distance, I am trying to compute the azimuth and then to recompute back one of the points.
However, the distance between the computed point and the original point is more then 50 meters, which is quite a big error.
Here is the code:
public static void main(String[] args) {
double startLongitude = -5.1085;
double startLatitude = 40.6682667;
double endLongitude = -4.000597497067124;
double endLatitude = 41.49682079962159;
double distance = 130947.51;
try {
CoordinateReferenceSystem crs = CRS.decode("EPSG:4326");
GeodeticCalculator calculator = new GeodeticCalculator(crs);
calculator.setStartingGeographicPoint(startLongitude, startLatitude);
calculator.setDestinationGeographicPoint(endLongitude, endLatitude);
double azimuth = calculator.getAzimuth();
System.out.println("Azimuth=" + azimuth);
calculator = new GeodeticCalculator(crs);
calculator.setStartingGeographicPoint(startLongitude, startLatitude);
calculator.setDirection(azimuth, distance);
Point2D computedEndPoint = calculator.getDestinationGeographicPoint();
System.out.println("computedEndPoint=" + computedEndPoint);
calculator = new GeodeticCalculator(crs);
calculator.setStartingGeographicPoint(endLongitude, endLatitude);
calculator.setDestinationGeographicPoint(computedEndPoint);
distance = calculator.getOrthodromicDistance();
System.out.println("Distance=" + distance);
} catch (FactoryException e) {
e.printStackTrace();
}
}
The output is:
Azimuth=44.97189638988797
computedEndPoint=Point2D.Double[-4.00014170719737, 41.49715519864095]
Distance=53.17698966547863
I expect the computedEndPoint to be quite similar (if not exactly) to declared end point from the beginning.
And the distance between these two points to be close to zero.
Now my question is: what am I doing wrong?
Or is there some bug in the GeodedicCalculator?
50m over 130km is 0.04% error - that is pretty good for a round trip of an iterative numerical method.
You are using the GeodedicCalculator which is using an approximation of the shape of the Earth for it's calculations. GeoTools uses the GeographicLib implementation of C. F. F. Karney, Algorithms for geodesics, J. Geodesy 87, 43–55 (2013), which explains the approximations used for solving the problem.
This answer on gis.stackexchange.com explains the level of accuracy you can expect from various numbers of decimal places when using latitude and longitude is also summarized in this XKCD cartoon:
Your lowest precision point is 4DP so you can't expect much better than 10's of metres from the rest of the calculation. You are unlikely to get much better than 5 DP of actual measurement even in the aviation domain and more likely to be working with 3DP and 10s-100s metres accuracy.
Update
Further investigation suggests that it is your original distance value that is wrong.
calculator.setStartingGeographicPoint(startLongitude, startLatitude);
calculator.setDestinationGeographicPoint(endLongitude, endLatitude);
double azimuth = calculator.getAzimuth();
System.out.println("Azimuth=" + azimuth);
double fullDistance = calculator.getOrthodromicDistance();
System.out.println("distance " + fullDistance);
System.out.println("% error " + (Math.abs(fullDistance - distance) / fullDistance) * 100);
calculator = new GeodeticCalculator(crs);
calculator.setStartingGeographicPoint(startLongitude, startLatitude);
calculator.setDirection(azimuth, fullDistance);
Point2D computedEndPoint = calculator.getDestinationGeographicPoint();
System.out.println("computedEndPoint=" + computedEndPoint);
calculator = new GeodeticCalculator(crs);
calculator.setStartingGeographicPoint(endLongitude, endLatitude);
calculator.setDestinationGeographicPoint(computedEndPoint);
distance = calculator.getOrthodromicDistance();
System.out.println("Distance=" + distance);
System.out.println("% error " + ((distance / fullDistance) * 100));
gives me:
Azimuth=44.971973670068415
distance 130893.86215735915
% error 0.04098575880994952
computedEndPoint=Point2D.Double[-4.000599999999989, 41.49682]
Distance=9.64120596409175E-10
% error 7.365666964965247E-13
As you can see all the error comes in the 1st calculation, the forward/reverse trip gives an identical point and 9e-10m error in distance. That should be fine for any domain.

What is initial bearing and final bearing

I am trying to calculate bearing between two lat/lon points as given in this link. I see that the bearing we get initially using the below equation is initial bearing.
public static double GetBearing(double latitude1, double longitude1, double latitude2, double longitude2)
{
var lat1 = ToRadians(latitude1);
var lat2 = ToRadians(latitude2);
var longdiff = ToRadians(longitude1 - longitude2);
var X = Math.Cos(lat2) * Math.Sin(longdiff);
var Y = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(longdiff);
var bearing =ToDegrees(Math.Atan2(X, Y));
return (bearing+360)%360;
}
It is given that
For final bearing, simply take the initial bearing from the end point to the start point and reverse it (using θ = (θ+180) % 360).
I am confused about the difference between initial bearing and final bearing.
What is this initial and final bearing and which bearing should we take as the final answer for bearing between two points.
The bearing is the angle between direction along the shortest path to destination and direction to North. The reason we have initial and final one is that we live on sphere, so the shortest path is geodesic line. It is a straight line on globe, bit if you draw it on flat map - it will be a curve.
There are two ways to think about it. Thinking on flat map: as you travel from A to B, this curve changes direction slightly, so the angle between this line and North changes, i.e. bearing changes.
Or you can think on sphere, and then think about triangle A - B - North Pole. The bearing is angle between between AB and appropriate meridian. Initial bearing is angle between AB and meridian crossing A. Final one is angle between AB and meridian crossing B. They are different.
The single "final answer" bearing only makes sense when distance between A and B is short. Then the curvature of Earth does not matter much, and the initial and final bearings are very close to each other, so depending on precision needed one can talk about single bearing.
FYI: bearing and many related computations are implemented in the R package geosphere
The bearing function returns the initial bearing, but you can invert the coordinates to get the final bearing.
library(geosphere)
bearing(cbind(0,0),cbind(20,20))
#[1] 43.4035
finalb <- bearing(cbind(20,20),cbind(0,0))
(finalb + 180) %% 360
#[1] 46.9656
(these results should be more precise than the ones you get with algorithm you refer to)
def bearing(lat1, lon1, lat2, lon2):
# Convert latitude and longitude to radians
lat1 = math.radians(lat1)
lon1 = math.radians(lon1)
lat2 = math.radians(lat2)
lon2 = math.radians(lon2)
y = math.sin(lon2-lon1) * math.cos(lat2)
x = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(lon2-lon1)
initial_bearing = math.degrees(math.atan2(y, x))
final_bearing = (initial_bearing + 180) % 360 if initial_bearing < 180 else (initial_bearing - 180) % 360
return initial_bearing, final_bearing

Google Map Road API not interpolating path and not giving smooth route

I am trying to get smooth route for below route path using road API in Roads Inspector but not getting smooth route.Google road api failing to provide smooth route.Some portion of route is not smooth with actual road route e.g at turns, at bridges etc. Please have look for below route path and provide solution to get smooth route/more accurate route along actual road.
Points:
42.04144333333333,-88.060575|42.04123666666667,-88.06014333333333|42.04119166666667,-88.06017166666666|42.040835,-88.05990166666666|42.03982666666667,-88.05242333333334|42.03903666666667,-88.04572333333333|42.03903833333333,-88.04572|42.038495,-88.04141833333334|42.03774666666666,-88.03593833333333|42.037683333333334,-88.03549|42.034861666666664,-88.03204166666667|42.02808,-88.031215|42.02045666666667,-88.03131166666667|42.012881666666665,-88.02988833333333|42.00522333333333,-88.02747666666667|41.997353333333336,-88.02500333333333|41.98961333333333,-88.02349333333333|41.982191666666665,-88.02351333333333|41.97412833333333,-88.02447333333333|41.96599,-88.02588166666666|41.95825833333333,-88.027075|41.952605,-88.03345|41.945281666666666,-88.0377|41.937595,-88.03779333333334|41.92935,-88.037845|41.92084333333333,-88.03799166666667|41.91231,-88.038075|41.90379,-88.038145|41.89564,-88.03784166666667|41.887255,-88.036495|41.87881,-88.03291666666667|41.87096833333333,-88.03694333333334|41.863101666666665,-88.04085166666667|41.85484833333334,-88.04049166666667|41.848978333333335,-88.03315166666667|41.842145,-88.02940833333334|41.83407,-88.02922|41.826135,-88.029025|41.820256666666666,-88.02674333333333|41.813515,-88.02884833333333|41.80965333333333,-88.03722166666667|41.810065,-88.04824833333333|41.8104,-88.06018333333333|41.81016666666667,-88.07216833333334|41.80704166666666,-88.08223833333334|41.80573666666667,-88.09275|41.80591166666667,-88.10409166666666|41.80608,-88.11518333333333|41.80625166666667,-88.12632166666667|41.806415,-88.13737333333333|41.80649666666667,-88.14849166666667|41.80653,-88.15959333333333|41.80652666666667,-88.17042666666667|41.805715,-88.181295|41.80482833333333,-88.19194333333333|41.803918333333336,-88.202765|41.80304666666667,-88.212815|41.802146666666665,-88.22354833333333|41.801383333333334,-88.23485666666667|41.80068833333333,-88.24686666666666|41.8,-88.25845333333334|41.799368333333334,-88.26976833333333|41.798743333333334,-88.28041666666667|41.80003166666667,-88.28312833333334|41.795566666666666,-88.28211666666667|41.79022,-88.28205833333334|41.785465,-88.28198|41.784135,-88.28193833333333|41.782473333333336,-88.283865|41.78230833333333,-88.28874666666667|41.782226666666666,-88.288225|41.781863333333334,-88.287305|41.78176833333333,-88.28751333333334|41.78176833333333,-88.28751333333334
points in the "inspector"
Your points don't look like the full output of a GPS tracker (the use case for which this API is designed), and thus you have stretches that are too far apart. Please increase the resolution of your GPS recording to get better output.
Google maps seems not to snap far points,
(if the distance between is longer than 300m - 400m).
As a solution I added some fake points between points that too far apart.
And now Google Maps snapping my route correctly.
List<LatLon> extendedPointsList = new List<LatLon>();
var lastLatLon = OrigionalRoute[0];
extendedPointsList.Add(lastLatLon);
indexesBeforeAddingPoints.Add(0);
for (int i = 1; i < OrigionalRoute.Count; i++)
{
var currentLatlon = OrigionalRoute[i];
double estimatedDistance =
getEstimatedDistanceBetweenPoints(currentLatlon, lastLatLon);
//estimated 400 meters
if (estimatedDistance > 0.004)
{
//estimated 340 meters
int countOfPoints = (int)Math.Round(estimatedDistance / 0.0034);
if (countOfPoints > 1)
{
var latDiff = (currentLatlon.Lat - lastLatLon.Lat) / countOfPoints;
var lonDiff = (currentLatlon.Lon - lastLatLon.Lon) / countOfPoints;
for (int j = 1; j < countOfPoints; j++)
{
var aveLat = lastLatLon.Lat + (latDiff * j);
var aveLon = lastLatLon.Lon + (lonDiff * j);
indexesBeforeAddingPoints.Add(i - 1);
extendedPointsList.Add(new LatLon(aveLat, aveLon));
}
}
}
indexesBeforeAddingPoints.Add(i);
extendedPointsList.Add(currentLatlon);
lastLatLon = currentLatlon;
}
OrigionalRoute = extendedPointsList;
The method to estimate distance (I ignored earth's sphere and projection).
private static double getEstimatedDistanceBetweenPoints(LatLon pointA, LatLon pointB)
{
return Math.Sqrt(Math.Pow((pointA.Lat - pointB.Lat), 2) + Math.Pow((pointA.Lon - pointB.Lon), 2));
}

Could someone explain me w.r.t. coordinates

Could someone please explain me what are w.r.t. coordinates? or at least direct me to a place that explains what they are? I've being searching for two days or so and all that I found is tutorials on how are they used but not what they actually are or even what wrt stand for.
These tutorials take the assumption I already know what they are which is stressful because I've never heard of them.
I'm working in as3 trying to do some parametric surfaces using pixel particles and I understand these are kind of useful while moving the particles around.
This is the relevant function where they are used as u,v and w, where p is a single particle that also contains xyz values that are not being modified.
function onEnter(evt:Event):void {
dphi = 0.015*Math.cos(getTimer()*0.000132);
dtheta = 0.017*Math.cos(getTimer()*0.000244);
phi = (phi + dphi) % pi2;
theta = (theta + dtheta) % pi2;
cost = Math.cos(theta);
sint = Math.sin(theta);
cosp = Math.cos(phi);
sinp = Math.sin(phi);
//We calculate some of the rotation matrix entries here for increased efficiency:
M11 = cost*sinp;
M12 = sint*sinp;
M31 = -cost*cosp;
M32 = -sint*cosp;
p = firstParticle;
//////// redrawing ////////
displayBitmapData.lock();
//apply filters pre-update
displayBitmapData.colorTransform(displayBitmapData.rect,darken);
displayBitmapData.applyFilter(displayBitmapData, displayBitmapData.rect, origin, blur);
p = firstParticle;
do {
//Calculate rotated coordinates
p.u = M11*p.x + M12*p.y + cosp*p.z;
p.v = -sint*p.x + cost*p.y;
p.w = M31*p.x + M32*p.y + sinp*p.z;
//Calculate viewplane projection coordinates
m = fLen/(fLen - p.u);
p.projX = p.v*m + projCenterX;
p.projY = p.w*m + projCenterY;
if ((p.projX > displayWidth)||(p.projX<0)||(p.projY<0)||(p.projY>displayHeight)||(p.u>uMax)) {
p.onScreen = false;
}
else {
p.onScreen = true;
}
if (p.onScreen) {
//we read the color in the position where we will place another particle:
readColor = displayBitmapData.getPixel(p.projX, p.projY);
//we take the blue value of this color to represent the current brightness in this position,
//then we increase this brightness by levelInc.
level = (readColor & 0xFF)+levelInc;
//we make sure that 'level' stays smaller than 255:
level = (level > 255) ? 255 : level;
/*
We create light blue pixels quickly with a trick:
the red component will be zero, the blue component will be 'level', and
the green component will be 50% of the blue value. We divide 'level' in
half using a fast technique: a bit-shift operation of shifting down by one bit
accomplishes the same thing as dividing by two (for an integer output).
*/
//dColor = ((level>>1) << 8) | level;
dColor = (level << 16) | (level << 8) | level;
displayBitmapData.setPixel(p.projX, p.projY, dColor);
}
p = p.next;
} while (p != null)
displayBitmapData.unlock();
}
This is the example I'm using http://www.flashandmath.com/flashcs4/light/
I kinda understand how are they used but I don't get why.
Thanks in advance.
PD: kind of surprised there is not even a tag related to it.
In that Particle3D.as class linked, they have:
//coords WRT viewpoint axes
public var u:Number;
public var v:Number;
public var w:Number;
From the code example you posted to the question it becomes clear that coords WRT viewpoint axes means coordinates with respect to viewpoint axes, since the code is doing exactly that .
What they are doing is a Camera (or Viewing) Transformation, where the Particle's world coordinates (x,y,z) is transformed from the world coordinate system to coordinates in the camera (or view) coordinate system (u,v,w).
(x,y,z) are the coordinates of the particle in the world coordinate system
(u,v,w) are the coordinates of the particle in the camera coordinate system
For example, the world coordinate system might have an origin at (0,0,0) with the camera positioned at something like (5,3,6) with an lookat vector of (1,0,0) and up vector of (0,1,0).

Retrieving destination coordinates from direction services by distance

I need to retrieve a destination's coordinates using the google maps api directions service. I already have the starting point coordinates, however instead of specifying an ending point in coordinates, I wish to retrieve the coordinates by specifying a distance (in km).
So I guess my question is the following: is it possible to retrieve the destination latlong coordinates (based/calculated on the road's distance and not directional/straight line) by specifying a distance (amount in km) with the directions service or perhaps any alternative way?
I have an image illustration, however unfortunately am unable to attach to this question as I do not have enough reputation. If my question is unclear in any way, or you wish to see the illustration then please contact me and I'll send it off.
I don't think you can do this as the request parameters say that origin and destination parameters are required.
I beliave it will help someone.
There is a method to get coordinates in the google maps library:
google.maps.geometry.spherical.computeOffset(fromCoordinates, distanceInMeters, headingInDegrees)
I believe you are correct. There doesn't seem to be any current method in the api which would allow you to do the following.
Instead I looped through the coordinates returned from the directions service call, and used a function to calculate the distance between coordinates. However even this was not accurate enough as the coordinates returned also seemed to be aggregated and doesn't return an accurate value/distance when calculating the distances between each coordinate as they are aggregated and therefore each coordinate is not necessary along the road.
To work around the above issue, I ended up adding a click event, and plotted the coordinates along the road myself and then stored them in a local json file which I cache and call using an xmlhttprequest.
Fortunately, for my situation I only need to calculate the distance between point A & B on one individual road, so my alternative won't work in cases when you're using multiple or generic roads/locations. You could instead use the first method described, given that you're happy to live with the aggregated data and an in-accurate calculation.
Below are the functions used to calculate the distances between coordinates and then also the final calculation to find the point & coordinates between the final two points. Please note this code relies on and uses jQuery methods.
1. Calculate distance (in meters) between two coordinates
function pointDistance( begin, end )
{
var begin = { lat: begin[0], long: begin[1] },
end = { lat: end[0], long: end[1] };
// General calculations
var earthRadius = 6371, //km
distanceLat = (end.lat - begin.lat).toRad(),
distanceLong = (end.long - begin.long).toRad();
// Convert lats to radiants
begin.lat = begin.lat.toRad();
end.lat = end.lat.toRad();
// Calculation
var a = Math.sin(distanceLat / 2) * Math.sin(distanceLat / 2) +
Math.sin(distanceLong / 2) * Math.sin(distanceLong / 2) * Math.cos(begin.lat) * Math.cos(end.lat);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var distance = (earthRadius * c) - 0.000536;
return (distance * 1000);
}
2. Fetch coordinate of final A-B coordinate (based on percentage remaining). The 'matrix' variable is a json array of coordinates.
function getCoordinates( totalDistance )
{
var lastPoint = { lat: null, long: null },
total = parseFloat(0),
position = { start: null, end: null, distance: 0 };
$(matrix).each(function()
{
if ( lastPoint.lat == null )
{
lastPoint = { lat: this[0], long: this[1] };
return;
}
var distance = pointDistance([lastPoint.lat, lastPoint.long], [this[0], this[1]]);
total = total + distance;
if ( (total / 1000) >= totalDistance )
{
position.start = new google.maps.LatLng(lastPoint.lat, lastPoint.long);
position.end = new google.maps.LatLng(this[0], this[1]);
position.distance = total;
return false;
}
lastPoint = { lat: this[0], long: this[1] };
});
return position;
}
3. Convert numeric degrees to radians
if ( typeof(Number.prototype.toRad) === 'undefined' ) {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
Hope the following helps any one with the same or simular problem. I haven't investigated this as I've had no need to, but, perhaps if you're dealing with google's paid services, they don't aggregate the data returned by the call?