Does anyone know the best way to go about getting the mid-point of a pair of latitude and longitude points?
I mm using d3.js to draw points on a map and need to draw a curved line between two points, so I need to create a mid point to draw a curve in the lines.
Please see image below for a better understanding of what I am trying to do:
The centroid of finitely many points is simply the arithmetic mean of each of the coordinates. So just sum up the latitudes and longitudes and divide by the number of points.
Finding the midpoint is calculated by taking the average of the x coordinates and then taking the average of the y coordinates. For example, if we had two coordinates located at (1,3) and (5, 7) our midpoint would be (3, 5).
Calculate the Halfway Point Using Coordinates Another solution is to simply do the math and calculate the midpoint using coordinates. First, you need to get the coordinates of each point. Then, add the latitudes of each, and divide by two. After that, add the longitudes of each location, and divide by two.
Apologies for the long script - it just seemed fun to draw stuff :-). I've marked off sections that are not required
// your latitude / longitude
var co2 = [70, 48];
var co1 = [-70, -28];
// NOT REQUIRED
var ctx = document.getElementById("myChart").getContext("2d");
function drawPoint(color, point) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI, false);
ctx.fill();
}
function drawCircle(point, r) {
ctx.strokeStyle = 'gray';
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.arc(point.x, point.y, r, 0, 2 * Math.PI, false);
ctx.stroke();
}
// REQUIRED
// convert to cartesian
var projection = d3.geo.equirectangular()
var cot1 = projection(co1);
var cot2 = projection(co2);
var p0 = { x: cot1[0], y: cot1[1] };
var p1 = { x: cot2[0], y: cot2[1] };
// NOT REQUIRED
drawPoint('green', p0);
drawPoint('green', p1);
// REQUIRED
function dfn(p0, p1) {
return Math.pow(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2), 0.5);
}
// from http://math.stackexchange.com/a/87374
var d = dfn(p0, p1);
var m = {
x: (p0.x + p1.x) / 2,
y: (p0.y + p1.y) / 2,
}
var u = (p1.x - p0.x) / d
var v = (p1.y - p0.y) / d;
// increase 1, if you want a larger curvature
var r = d * 1;
var h = Math.pow(Math.pow(r, 2) - Math.pow(d, 2) / 4, 0.5);
// 2 possible centers
var c1 = {
x: m.x - h * v,
y: m.y + h * u
}
var c2 = {
x: m.x + h * v,
y: m.y - h * u
}
// NOT REQUIRED
drawPoint('gray', c1)
drawPoint('gray', c2)
drawCircle(c1, r)
drawCircle(c2, r)
// REQUIRED
// from http://math.stackexchange.com/a/919423
function mfn(p0, p1, c) {
// the -c1 is for moving the center to 0 and back again
var mt1 = {
x: r * (p0.x + p1.x - c.x * 2) / Math.pow(Math.pow(p0.x + p1.x - c.x * 2, 2) + Math.pow(p0.y + p1.y - c.y * 2, 2), 0.5)
};
mt1.y = (p0.y + p1.y - c.y * 2) / (p0.x + p1.x - c.x * 2) * mt1.x;
var ma = {
x: mt1.x + c.x,
y: mt1.y + c.y,
}
var mb = {
x: -mt1.x + c.x,
y: -mt1.y + c.y,
}
return (dfn(ma, p0) < dfn(mb, p0)) ? ma : mb;
}
var m1 = mfn(p0, p1, c1);
var m2 = mfn(p0, p1, c2);
var mo1 = projection.invert([m1.x, m1.y]);
var mo2 = projection.invert([m2.x, m2.y]);
// NOT REQUIRED
drawPoint('blue', m1);
drawPoint('blue', m2);
// your final output (in lat long)
console.log(mo1);
console.log(mo2);
Fiddle - https://jsfiddle.net/srjuc2gd/
And here's just the relevant portion (most of it is just copy-pasta from the beginning of this answer)
var Q31428016 = (function () {
// adjust curvature
var CURVATURE = 1;
// project to convert from lat / long to cartesian
var projection = d3.geo.equirectangular();
// distance between p0 and p1
function dfn(p0, p1) {
return Math.pow(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2), 0.5);
}
// mid point between p0 and p1
function cfn(p0, p1) {
return {
x: (p0.x + p1.x) / 2,
y: (p0.y + p1.y) / 2,
}
}
// get arc midpoint given end points, center and radius - http://math.stackexchange.com/a/919423
function mfn(p0, p1, c, r) {
var m = cfn(p0, p1);
// the -c1 is for moving the center to 0 and back again
var mt1 = {
x: r * (m.x - c.x) / Math.pow(Math.pow(m.x - c.x, 2) + Math.pow(m.y - c.y, 2), 0.5)
};
mt1.y = (m.y - c.y) / (m.x - c.x) * mt1.x;
var ma = {
x: mt1.x + c.x,
y: mt1.y + c.y,
}
var mb = {
x: -mt1.x + c.x,
y: -mt1.y + c.y,
}
return (dfn(ma, p0) < dfn(mb, p0)) ? ma : mb;
}
var Q31428016 = {};
Q31428016.convert = function (co1, co2) {
// convert to cartesian
var cot1 = projection(co1);
var cot2 = projection(co2);
var p0 = { x: cot1[0], y: cot1[1] };
var p1 = { x: cot2[0], y: cot2[1] };
// get center - http://math.stackexchange.com/a/87374
var d = dfn(p0, p1);
var m = cfn(p0, p1);
var u = (p1.x - p0.x) / d
var v = (p1.y - p0.y) / d;
var r = d * CURVATURE;
var h = Math.pow(Math.pow(r, 2) - Math.pow(d, 2) / 4, 0.5);
// 2 possible centers
var c1 = {
x: m.x - h * v,
y: m.y + h * u
}
var c2 = {
x: m.x + h * v,
y: m.y - h * u
}
// get arc midpoints
var m1 = mfn(p0, p1, c1, r);
var m2 = mfn(p0, p1, c2, r);
// convert back to lat / long
var mo1 = projection.invert([m1.x, m1.y]);
var mo2 = projection.invert([m2.x, m2.y]);
return [mo1, mo2]
}
return Q31428016;
})();
// your latitude / longitude
var co1 = [-70, -28];
var co2 = [70, 48];
var mo = Q31428016.convert(co1, co2)
// your output
console.log(mo[0]);
console.log(mo[1]);
For the accurate side:
You may use the Esri web API. Nothing beats decades of experience implementing the hard side of projection systems and datums... Athough the ArcGIS for Server line is a commercial product, the JS API is free, and here there's a pure JS function that does just what you want : geometryEngine.densify ; that function requires an interval parameter, that you can, in your case, get by dividing by two the results of geometryEngine.geodesicLength
That'll need you to get acquainted with the Polyline class in a very basic way, such as var mySegment = new Polyline([[50,3], [55,8]]);
and probably nothing further.
For the visual side :
Your segment has two middles ? You may also be interested in geometryEngine.offset ; first offset the original segment once in each direction, then get the center point for each of the resulting segments.
For the practical side :
Given the short distances involved, provided you're not dealing with a weird place that'd be too close to the poles, I'd simply go with an arithmetic average of X and Y, and then add/subtract a rotated vector to offset your two "middles". Doing it this way would be both lighter on the machine (no libs to load from a CDN), easier on you, and as long as the purpose of it is a nice display, the result will be more than good enough.
(added as per comment : a sample)
// Your known starting points, and a k factor of your choice.
var a = {x:3, y:50};
var b = {x:8, y:55};
var k = 0.2;
// Intermediate values
var vab = {x:b.x-a.x, y:b.y-a.y};
var v_rotated = {x:-k*vab.y, y:k*vab.x};
var middle = {x:a.x+vab.x/2, y:a.y+vab.y/2};
// Your two resulting points
var result_i = {x: middle.x + v_rotated.x, y: middle.y + v_rotated.y};
var result_j = {x: middle.x - v_rotated.x, y: middle.y - v_rotated.y};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With