The Google Earth Desktop Application shows the both the map length
and ground length
of a line.
In the Google Earth plugin I want to do a similar thing, that is I wish to determine the ground length
of a tessellated KmlLineString
taking the terrain into account.
Can I do this, and if so, how?
You can certainly get the length pretty easily if you use the earth-api-utility-library. Using that you can do.
var length = (new geo.Path(linestring)).distance();
Granted this method does not take the terrain into account - but there are a number of caveats you should be aware of before trying calculate distances using an elevation gradient.
Firstly any differences between topographic and direct distance are minimal in most cases. Indeed many quality GPS receivers simply don't take any changes in elevation into account when calculating distances.
Secondly ground altitude is one of the most unreliable pieces data. Using a gradient based on elevation to determine distance will often produce greater inaccuracy in distance measurements than using a simple 'as the crow flies' measure.
Bearing that in mind, if you still wanted to do it then one way would be something like the following.
You can improve your precision of this kind of method in two ways, either by increasing the sampling rate (say every meter) or by applying a smoothing procedure to the results.
For a rougher version, you could just loop over the coordinates in the the KmlLinestring
itself, rather than resampling at some set distance. You would use the latitude, longitude of the coordinate to get the ground altitude at each point. Then you would construct a Cartesian coordinate from this data (latitude, longitude, elevation => X,Y,Z) and work out the angular distance between it and the next point...and so on.
something like the following idea should work - although it is written here and untested!
var EARTH_RADIUS = 6378135; // approximate in meters
var degreestoRadians = function(degrees) {
return degrees * Math.PI / 180;
}
var arcLength = function(point1 , point2) {
var length = Math.sqrt(Math.pow(point1.X-point2.X, 2)
+ Math.pow(point1.Y-point2.Y, 2)
+ Math.pow(point1.Z-point2.Z, 2));
var angle = 2 * Math.asin(length/2/EARTH_RADIUS);
return EARTH_RADIUS * angle;
}
var sphericalToCartesian = function(latitude, longitude, altitude) {
var phi = degreestoRadians(latitude);
var theta = degreestoRadians(longitude);
var rho = EARTH_RADIUS + altitude;
return {
X: Math.cos(phi) * Math.cos(theta) * rho,
Y: Math.cos(phi) * Math.sin(theta) * rho,
Z: Math.sin(phi) * rho
}
}
var topographicDistance = function(linestring) {
var coordinates = linestring.getCoordinates(); //KmlCoordArray
var last = null;
var distance = 0;
for(var i = 0; i < coordinates.length; i++) {
var coord = coordinates.get(i); //KmlCoord
var lat = coord.getLatitude();
var lng = coord.getLongitude();
var alt = ge.getGlobe().getGroundAltitude(lat, lng);
var latest = sphericalToCartesian(lat, lng, alt);
if(last != null) {
distance += arcLength(last, latest);
}
last = latest;
}
return distance;
}
You would use it like so...
var distance = topographicDistance(yourLinestring);