Skip to content

Instantly share code, notes, and snippets.

@jgbos
Last active December 16, 2015 08:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jgbos/5409595 to your computer and use it in GitHub Desktop.
Save jgbos/5409595 to your computer and use it in GitHub Desktop.
Time of Flight From North Korea
<!DOCTYPE html>
<meta charset="utf-8">
<title>Time of Flight from North Korea</title>
<style>
body {
background: #fcfcfc;
}
.background {
fill: #fff;
}
.outline {
stroke: #000;
stroke-width: 1px;
fill: none;
}
.land {
fill: #fff;
}
.land-glow {
fill: #000;
fill-opacity: .5;
filter: url(#glow);
}
.border {
stroke: #000;
stroke-width: .5px;
fill: none;
}
div {
width:300px;
float: left;
}
.circle {
stroke: #f00;
stroke-width: .5px;
fill: none;
}
.major {
stroke-width: 1.5px;
stroke-dasharray: none;
}
.graticule {
stroke: #99f;
stroke-width: .5px;
stroke-opacity: .3;
fill: none;
}
.caption {
font-style: italic;
}
p {
position: absolute;
top: 50px;
color: #000;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 14px;
text-align: center;
width: 300px;
}
</style>
<body>
<div id="map1"><p> Equidistant, Inertial</div>
<div id="map2"><p> Equidistant, Rotating</div>
<div id="map3"><p> Equal-??, Rotating</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var earth = {
Re: 6371000,
mu: 3.986004418e14,
rotation_rate : 7292115.1467e-11
};
var width = 1200,
height = 500;
var origin = [127.2565, 40.2012],
degrees = 180 / Math.PI,
radians = Math.PI / 180;
var equidistant = d3.geo.azimuthalEquidistant()
.translate([150,250])
.clipAngle(120);
var equiflight = d3.geo.projection(function(λ, φ) {
var cosλ = Math.cos(λ),
cosφ = Math.cos(φ),
c = Math.acos(cosλ * cosφ);
if (c < 10*radians){
tf = c;
}
else {
tf = c && flightTime(origin,c);
}
var p = toLLA(rotate([Math.cos(φ)*Math.cos(λ),
Math.cos(φ)*Math.sin(λ),
Math.sin(φ)], tf));
var c = Math.acos(Math.cos(p[0]*radians) * Math.cos(p[1]*radians)),
k = c && c / Math.sin(c);
return [
k * cosφ * Math.sin(λ),
k * Math.sin(φ)
];
})
.translate([300,250])
.clipAngle(120);
function northKorea(projection) {
return projection
.scale(70)
.rotate([-origin[0], -origin[1], 0])
.precision(.1);
}
var path = d3.geo.path().pointRadius(2.5);
circle = d3.geo.circle().origin(origin),
format = d3.format(",d");
var ellipse = function (data,φ){
data.coordinates[0] = data.coordinates[0].map(function (d){
var tf = flightTime(origin,φ*radians),
xeci = toCartesian(d),
xecef = rotate(xeci, tf); // rotates earth
return toLLA(xecef);
});
return data;
}
var svg = d3.selectAll("#map1, #map2, #map3")
.data([equidistant, equidistant, equiflight].map(northKorea))
.append("svg");
svg.each(function (projection){
var t = projection.translate();
d3.select(this)
.attr("width", 2 * t[0])
.attr("height", 2 * t[1]);
})
svg.append("filter")
.attr("id", "glow")
.append("feGaussianBlur")
.attr("stdDeviation", 3);
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "background");
var g = svg.append("g").attr("class","map");
svg.append("path")
.attr("class", "graticule")
.datum(d3.geo.graticule()());
svg.each(function (projection,k){
var angle = projection.clipAngle(),
t = projection.translate(),
c = function (d){
if (k==0){
return circle.angle(d)();
}
else {
return ellipse(circle.angle(d)(),d);
}
};
var g = d3.select(this);
var δ = 1e6 / earth.Re * degrees;
g.selectAll("path.circle")
.data(d3.range(δ, angle, δ))
.enter().append("path")
.datum(function(d) { return c(d);})
.attr("class", function(_, i) { return (i + 1) % 5 ? "circle" : "major circle"; });
g.selectAll("text")
.data(projection.clipAngle() > 90 ? [5000, 10000] : [5000])
.enter().append("text")
.attr("dy", "-.35em")
.append("textPath")
.attr("xlink:href", function(_, i) { return "#text-" + angle + "-" + i; })
.text(function(d) { return format(d) + "km"; });
});
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "outline");
svg.append("path")
.datum({type: "Point", coordinates: origin});
svg.each(redraw);
d3.json("/d/4090846/world-50m.json", function(error, world) {
var land = topojson.feature(world, world.objects.land);
g.append("path")
.datum(land)
.attr("class", "land-glow");
g.append("path")
.datum(land)
.attr("class", "land");
g.append("path")
.datum(topojson.mesh(world, world.objects.countries))
.attr("class", "border");
g.each(redraw);
});
function redraw(projection) {
d3.select(this).selectAll("path").attr("d", path.projection(projection));
}
function rotate(pos,time){
var ω = earth.rotation_rate,
ct = Math.cos(time*ω),
st = Math.sin(time*ω);
return [
ct * pos[0] + st*pos[1],
-st * pos[0] + ct*pos[1],
pos[2]
];
}
function flightTime(launch, φ){
// Earth Parameters
var μ = earth.mu,
Re = earth.Re,
γ = 45 * Math.PI/180;
// Calculate earth center vector length at latitude, longitude
var R0 = toCartesian(launch),
r0 = Math.sqrt(R0.reduce(function (a,b){return a+b*b;}));
// Velocity of Target
var V = Math.sqrt(μ*(1-Math.cos(φ))/ (r0*Math.cos(γ)*(r0*Math.cos(γ)/Re - Math.cos(φ+γ))));
// Constant to calculate flight time
var λ = r0*V*V/μ;
// Calculate Flight Time
var cg = Math.cos(γ),
sg = Math.sin(γ),
tg = Math.tan(γ),
cp = Math.cos(φ),
sp = Math.sin(φ),
cgp = Math.cos(γ + φ),
cot = 1 / Math.tan(φ/2);
var first_term = (tg*(1-cp) + (1-λ)*sp) / ((2-λ)*((1-cp)/(λ*cg*cg) + cgp/cg)),
sec_term = 2*cg / (λ*Math.pow(2/λ-1,1.5)) * Math.atan2(Math.sqrt(2/λ-1),(cg*cot-sg));
return r0 / (V*cg) * (first_term + sec_term);
}
function toCartesian(gc) {
var lat = gc[1]*radians,
lon = gc[0]*radians,
R = earth.Re;
var x = R*Math.cos(lat)*Math.cos(lon),
y = R*Math.cos(lat)*Math.sin(lon),
z = R*Math.sin(lat);
return [x,y,z];
}
function toLLA(position){
var x = position[0],
y = position[1],
z = position[2],
r = Math.sqrt(x*x + y*y);
// Longitude
var lon = Math.atan2(y, x),
lat = Math.atan2(z,r);
return [lon*degrees, lat*degrees];
}
d3.select(self.frameElement).style("height", height + "px");
d3.select(self.frameElement).style("width", width + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment