Skip to content

Commit

Permalink
Merge pull request IITC-CE#3 from IITC-CE/refactor
Browse files Browse the repository at this point in the history
Refactor for easy options/methods overriding
  • Loading branch information
johnd0e authored Feb 24, 2020
2 parents 39c4fba + deb6da8 commit c9f84ab
Showing 1 changed file with 103 additions and 92 deletions.
195 changes: 103 additions & 92 deletions src/L.Geodesic.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,120 +4,139 @@
var r2d = 180.0/Math.PI;
var earthR = 6367000.0; // earth radius in meters (doesn't have to be exact)

function geodesicPoly(Klass, fill) {
return Klass.extend({
initialize: function (latlngs, options) {
Klass.prototype.initialize.call(this, L.geodesicConvertLines(latlngs, fill), options);
this._latlngsinit = this._convertLatLngs(latlngs);
},
getLatLngs: function () {
return this._latlngsinit;
},
setLatLngs: function (latlngs) {
this._latlngsinit = this._convertLatLngs(latlngs);
return this.redraw();
},
addLatLng: function (latlng) {
this._latlngsinit.push(L.latLng(latlng));
return this.redraw();
},
redraw: function() {
this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
return Klass.prototype.redraw.call(this);
}
});
}

// alternative geodesic line intermediate points function
// as north/south lines have very little curvature in the projection, we can use longitude (east/west) seperation
// to calculate intermediate points. hopefully this will avoid the rounding issues seen in the full intermediate
// points code that have been seen
function geodesicConvertLine(startLatLng, endLatLng, convertedPoints) {
function geodesicConvertLine (start, end, convertedPoints) { // push intermediate points into convertedPoints

// maths based on https://edwilliams.org/avform.htm#Int
var lng1 = start.lng * d2r;
var lng2 = end.lng * d2r;
var dLng = lng1-lng2;

var lat1 = startLatLng.lat * d2r;
var lat2 = endLatLng.lat * d2r;
var lng1 = startLatLng.lng * d2r;
var lng2 = endLatLng.lng * d2r;
var segments = Math.floor(Math.abs(dLng * earthR / this.options.segmentsCoeff));
if (segments < 2) { return; }

var dLng = lng2-lng1;

var segments = Math.floor(Math.abs(dLng * earthR / 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);
// maths based on https://edwilliams.org/avform.htm#Int

var sinLat1CosLat2 = sinLat1*cosLat2;
var sinLat2CosLat1 = sinLat2*cosLat1;
// pre-calculate some constant values for the loop
var lat1 = start.lat * d2r;
var lat2 = end.lat * d2r;
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(iLng-lng2) - sinLat2CosLat1 * Math.sin(iLng-lng1))
/ cosLat1CosLat2SinDLng
);
convertedPoints.push(L.latLng(iLat*r2d, iLng*r2d));
}
}

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);
// iterate pairs of connected vertices with fn(), adding new intermediate vertices (if returned)
function processPoly (latlngs, fn) {
var result = [];

var point = L.latLng ( [iLat*r2d, iLng*r2d] );
convertedPoints.push(point);
}
// var isPolygon = this.options.fill; // !wrong: L.Draw use options.fill with polylines
var isPolygon = this instanceof L.Polygon;
if (isPolygon) {
latlngs.push(latlngs[0]);
} else {
result.push(latlngs[0]);
}

convertedPoints.push(L.latLng(endLatLng));
for (var i = 0, len = latlngs.length - 1; i < len; i++) {
fn.call(this, latlngs[i], latlngs[i+1], result);
result.push(latlngs[i+1]);
}
return result;
}



L.geodesicConvertLines = function (latlngs, fill) {
function geodesicConvertLines (latlngs) {
if (latlngs.length === 0) {
return [];
}

for (var i = 0, len = latlngs.length; i < len; i++) {
if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
return;
}
latlngs[i] = L.latLng(latlngs[i]);
}

// geodesic calculations have issues when crossing the anti-meridian. so offset the points
// so this isn't an issue, then add back the offset afterwards
// a center longitude would be ideal - but the start point longitude will be 'good enough'
var lngOffset = latlngs[0].lng;

// points are wrapped after being offset relative to the first point coordinate, so they're
// within +-180 degrees
latlngs = latlngs.map(function(a){ return L.latLng(a.lat, a.lng-lngOffset).wrap(); });

var geodesiclatlngs = [];
latlngs = latlngs.map(function (a) { return L.latLng(a.lat, a.lng-lngOffset).wrap(); });

if(!fill) {
geodesiclatlngs.push(latlngs[0]);
}
for (i = 0, len = latlngs.length - 1; i < len; i++) {
geodesicConvertLine(latlngs[i], latlngs[i+1], geodesiclatlngs);
}
if(fill) {
geodesicConvertLine(latlngs[len], latlngs[0], geodesiclatlngs);
}
var geodesiclatlngs = this._processPoly(latlngs,this._geodesicConvertLine);

// now add back the offset subtracted above. no wrapping here - the drawing code handles
// things better when there's no sudden jumps in coordinates. yes, lines will extend
// beyond +-180 degrees - but they won't be 'broken'
geodesiclatlngs = geodesiclatlngs.map(function(a){ return L.latLng(a.lat, a.lng+lngOffset); });
geodesiclatlngs = geodesiclatlngs.map(function (a) { return L.latLng(a.lat, a.lng+lngOffset); });

return geodesiclatlngs;
}

var polyOptions = {
segmentsCoeff: 5000
};

var PolyMixin = {
_geodesicConvertLine: geodesicConvertLine,

_processPoly: processPoly,

_geodesicConvertLines: geodesicConvertLines,

_geodesicConvert: function () {
this._latlngs = this._geodesicConvertLines(this._latlngsinit);
this._convertLatLngs(this._latlngs); // update bounds
},

options: polyOptions,

initialize: function (latlngs, options) {
L.Polyline.prototype.initialize.call(this, latlngs, options);
this._geodesicConvert();
},

getLatLngs: function () {
return this._latlngsinit;
},

_setLatLngs: function (latlngs) {
this._bounds = L.latLngBounds();
this._latlngsinit = this._convertLatLngs(latlngs);
},

_defaultShape: function () {
var latlngs = this._latlngsinit;
return L.LineUtil.isFlat(latlngs) ? latlngs : latlngs[0];
},

redraw: function () {
this._geodesicConvert();
return L.Path.prototype.redraw.call(this);
}
};

L.GeodesicPolyline = geodesicPoly(L.Polyline, false);
L.GeodesicPolygon = geodesicPoly(L.Polygon, true);
L.GeodesicPolyline = L.Polyline.extend(PolyMixin);

PolyMixin.options = polyOptions; // workaround for https://github.com/Leaflet/Leaflet/pull/6766/
L.GeodesicPolygon = L.Polygon.extend(PolyMixin);

L.GeodesicCircle = L.Polygon.extend({
options: {
segmentsCoeff: 1000,
segmentsMin: 48
},

initialize: function (latlng, options, legacyOptions) {
if (typeof options === 'number') {
// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
Expand All @@ -129,10 +148,6 @@
L.Polygon.prototype.initialize.call(this, points, options);
},

options: {
fill: true
},

setLatLng: function (latlng) {
this._latlng = L.latLng(latlng);
var points = this._calcPoints();
Expand All @@ -149,18 +164,15 @@
return this._latlng;
},

getRadius: function() {
getRadius: function () {
return this._radius;
},

_calcPoints: function() {
//console.log("geodesicCircle: radius = "+this._radius+"m, centre "+this._latlng.lat+","+this._latlng.lng);
_calcPoints: function () {

// circle radius as an angle from the centre of the earth
var radRadius = this._radius / earthR;

//console.log(" (radius in radians "+radRadius);

// pre-calculate various values used for every point on the circle
var centreLat = this._latlng.lat * d2r;
var centreLng = this._latlng.lng * d2r;
Expand All @@ -171,26 +183,25 @@
var cosRadRadius = Math.cos(radRadius);
var sinRadRadius = Math.sin(radRadius);

var calcLatLngAtAngle = function(angle) {
var calcLatLngAtAngle = function (angle) {
var lat = Math.asin(sinCentreLat*cosRadRadius + cosCentreLat*sinRadRadius*Math.cos(angle));
var lng = centreLng + Math.atan2(Math.sin(angle)*sinRadRadius*cosCentreLat, cosRadRadius-sinCentreLat*Math.sin(lat));

return L.latLng(lat * r2d,lng * r2d);
};


var segments = Math.max(48,Math.floor(this._radius/1000));
//console.log(" (drawing circle as "+segments+" lines)");
var o = this.options;
var segments = Math.max(o.segmentsMin,Math.floor(this._radius/o.segmentsCoeff));
var points = [];
for (var i=0; i<segments; i++) {
var angle = Math.PI*2/segments*i;

var point = calcLatLngAtAngle(angle);
points.push ( point );
points.push(point);
}

return points;
},
}

});

Expand Down

0 comments on commit c9f84ab

Please sign in to comment.