geodesic line drawing: alternative intermediate point calculation code - based on simpler lon->lat formula rather than N intermediate points

more than good enough for line drawing, and hopefully will avoid odd rounding issues seen on mobile
This commit is contained in:
Jon Atkins 2013-12-19 03:30:57 +00:00
parent 0dff718f1f
commit e536fb6d12

View File

@ -34,52 +34,54 @@ Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
}
});
}
function geodesicConvertLine(startLatlng, endLatlng, convertedPoints) {
var i,
R = 6378137, // earth radius in meters (doesn't have to be exact)
maxlength = 5000, // meters before splitting
d2r = L.LatLng.DEG_TO_RAD,
r2d = L.LatLng.RAD_TO_DEG,
lat1, lat2, lng1, lng2, dLng, d, segments,
f, A, B, x, y, z, fLat, fLng;
dLng = (endLatlng.lng - startLatlng.lng) * d2r;
lat1 = startLatlng.lat * d2r;
lat2 = endLatlng.lat * d2r;
lng1 = startLatlng.lng * d2r;
lng2 = endLatlng.lng * d2r;
// http://en.wikipedia.org/wiki/Great-circle_distance
d = Math.atan2(Math.sqrt( Math.pow(Math.cos(lat2) * Math.sin(dLng), 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng), 2) ), Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLng));
// alternative geodesic line intermediate points function
// as north/south lines have very little curvature in the projection, we cam use longitude (east/west) seperation
// to calculate intermediate points. hopeefully this will avoid the rounding issues seen in the full intermediate
// points code that have been seen
function geodesicConvertLine(startLatLng, endLatLng, convertedPoints) {
var R = 6378137; // earth radius in meters (doesn't have to be exact)
var d2r = L.LatLng.DEG_TO_RAD;
var r2d = L.LatLng.RAD_TO_DEG;
segments = Math.ceil(d * R / maxlength);
// loop starts at 1 - we don't add the very first point
// loop ends before 'segments' is reached - we don't add the very last point here but outside the loop
// (this was to fix a bug - https://github.com/jonatkins/ingress-intel-total-conversion/issues/471
// rounding errors? maths bug? not sure - but it solves the issue! and is a slight optimisation)
// UPDATE: there still seem to be rounding errors on relatively short links - but only on mobile.
// let's only add intermediate points if there's two or more
if (segments >= 3) {
for (i = 1; i < segments; i++) {
// http://williams.best.vwh.net/avform.htm#Intermediate
// modified to handle longitude above +-180 degrees
f = i / segments;
A = Math.sin((1-f)*d) / Math.sin(d);
B = Math.sin(f*d) / Math.sin(d);
x = A * Math.cos(lat1) * Math.cos(0) + B * Math.cos(lat2) * Math.cos(dLng);
y = A * Math.cos(lat1) * Math.sin(0) + B * Math.cos(lat2) * Math.sin(dLng);
z = A * Math.sin(lat1) + B * Math.sin(lat2);
fLat = r2d * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
fLng = r2d * (Math.atan2(y, x)+lng1);
// maths based on http://williams.best.vwh.net/avform.htm#Int
convertedPoints.push(L.latLng([fLat, fLng]));
var lat1 = startLatLng.lat * d2r;
var lat2 = endLatLng.lat * d2r;
var lng1 = startLatLng.lng * d2r;
var lng2 = endLatLng.lng * d2r;
var dLng = lng2-lng1;
var segments = Math.floor(Math.abs(dLng * R / 5000));
if (segments > 1) {
// pre-calculate some constant values for the loop
var sinLat1 = Math.sin(lat1);
var sinLat2 = Math.sin(lat2);
var cosLat1 = Math.cos(lat1);
var cosLat2 = Math.cos(lat2);
var sinLat1CosLat2 = sinLat1*cosLat2;
var sinLat2CosLat1 = sinLat2*cosLat1;
var cosLat1CosLat2SinDLng = cosLat1*cosLat2*Math.sin(dLng);
for (var i=1; i < segments; i++) {
var iLng = lng1+dLng*(i/segments);
var iLat = Math.atan( (sinLat1CosLat2*Math.sin(lng2-iLng) + sinLat2CosLat1*Math.sin(iLng-lng1))
/ cosLat1CosLat2SinDLng)
var point = L.latLng ( [iLat*r2d, iLng*r2d] );
convertedPoints.push(point);
}
}
// push the final point unmodified
convertedPoints.push(L.latLng(endLatlng));
convertedPoints.push(L.latLng(endLatLng));
}
L.geodesicConvertLines = function (latlngs, fill) {
if (latlngs.length == 0) {
return [];