Skip to content

Commit

Permalink
Merge branch 'greatcircle' of https://github.com/jasondavies/d3 into …
Browse files Browse the repository at this point in the history
…release
  • Loading branch information
mbostock committed Aug 29, 2011
2 parents fa312bf + c060439 commit 1e60010
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 1 deletion.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ d3.geo.js: \
src/geo/mercator.js \
src/geo/path.js \
src/geo/bounds.js \
src/geo/greatCircle.js \
src/end.js

d3.csv.js: \
Expand Down
91 changes: 91 additions & 0 deletions d3.geo.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,95 @@ function d3_geo_boundsPolygon(o, f) {
f.apply(null, a[i]);
}
}
// From http://williams.best.vwh.net/avform.htm#Intermediate
d3.geo.greatCircle = function() {
var source = d3_geo_greatCircleSource,
target = d3_geo_greatCircleTarget,
n = 100,
radius = 6371; // Mean radius of Earth, in km.
// TODO: breakAtDateLine?

function greatCircle(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
cx0 = Math.cos(x0), sx0 = Math.sin(x0),
cy0 = Math.cos(y0), sy0 = Math.sin(y0),
cx1 = Math.cos(x1), sx1 = Math.sin(x1),
cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = Math.acos(sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)),
sd = Math.sin(d),
f = d / (n - 1),
e = -f,
path = [],
i = -1;

while (++i < n) {
e += f;
var A = Math.sin(d - e) / sd,
B = Math.sin(e) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
path[i] = [
Math.atan2(y, x) / d3_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians
];
}

return path;
}

greatCircle.source = function(x) {
if (!arguments.length) return source;
source = x;
return greatCircle;
};

greatCircle.target = function(x) {
if (!arguments.length) return target;
target = x;
return greatCircle;
};

greatCircle.n = function(x) {
if (!arguments.length) return n;
n = +x;
return greatCircle;
};

greatCircle.radius = function(x) {
if (!arguments.length) return radius;
radius = +x;
return greatCircle;
};

// Haversine formula for great-circle distance.
greatCircle.distance = function(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
sy = Math.sin((y1 - y0) / 2),
sx = Math.sin((x1 - x0) / 2),
a = sy * sy + Math.cos(y0) * Math.cos(y1) * sx * sx;

return radius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};

return greatCircle;
};

function d3_geo_greatCircleSource(d) {
return d.source;
}

function d3_geo_greatCircleTarget(d) {
return d.target;
}
})();
2 changes: 1 addition & 1 deletion d3.geo.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 91 additions & 0 deletions src/geo/greatCircle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// From http://williams.best.vwh.net/avform.htm#Intermediate
d3.geo.greatCircle = function() {
var source = d3_geo_greatCircleSource,
target = d3_geo_greatCircleTarget,
n = 100,
radius = 6371; // Mean radius of Earth, in km.
// TODO: breakAtDateLine?

function greatCircle(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
cx0 = Math.cos(x0), sx0 = Math.sin(x0),
cy0 = Math.cos(y0), sy0 = Math.sin(y0),
cx1 = Math.cos(x1), sx1 = Math.sin(x1),
cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = Math.acos(sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)),
sd = Math.sin(d),
f = d / (n - 1),
e = -f,
path = [],
i = -1;

while (++i < n) {
e += f;
var A = Math.sin(d - e) / sd,
B = Math.sin(e) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
path[i] = [
Math.atan2(y, x) / d3_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians
];
}

return path;
}

greatCircle.source = function(x) {
if (!arguments.length) return source;
source = x;
return greatCircle;
};

greatCircle.target = function(x) {
if (!arguments.length) return target;
target = x;
return greatCircle;
};

greatCircle.n = function(x) {
if (!arguments.length) return n;
n = +x;
return greatCircle;
};

greatCircle.radius = function(x) {
if (!arguments.length) return radius;
radius = +x;
return greatCircle;
};

// Haversine formula for great-circle distance.
greatCircle.distance = function(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
sy = Math.sin((y1 - y0) / 2),
sx = Math.sin((x1 - x0) / 2),
a = sy * sy + Math.cos(y0) * Math.cos(y1) * sx * sx;

return radius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};

return greatCircle;
};

function d3_geo_greatCircleSource(d) {
return d.source;
}

function d3_geo_greatCircleTarget(d) {
return d.target;
}
42 changes: 42 additions & 0 deletions test/geo/greatCircle-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require("../env");
require("../../d3");
require("../../d3.geo");

var vows = require("vows"),
assert = require("assert");

var suite = vows.describe("d3.geo.greatCircle");

suite.addBatch({
"greatCircle": {
topic: function() {
return d3.geo.greatCircle()
.n(12);
},
"distance": function(circle) {
assert.equal(circle.distance({source: [0, 0], target: [0, 0]}), 0);
assert.inDelta(circle.distance({
source: [118 + 24 / 60, 33 + 57 / 60],
target: [ 73 + 47 / 60, 40 + 38 / 60]
}), 3973, .5);
},
"geodesic": function(circle) {
assert.inDelta(circle({source: [5, 52], target: [-120, 37]}), [
[ 5, 52 ],
[ -3.805036, 57.05083],
[ -15.122869, 61.30118],
[ -29.396213, 64.34584],
[ -46.132729, 65.72409],
[ -63.394401, 65.15597],
[ -78.854311, 62.76337],
[ -91.401599, 58.96701],
[-101.190927, 54.21333],
[-108.843633, 48.83586],
[-114.961152, 43.05231],
[-120, 37 ]
], .5);
}
}
});

suite.export(module);

0 comments on commit 1e60010

Please sign in to comment.