Skip to content

Instantly share code, notes, and snippets.

@zanarmstrong
Last active May 16, 2017 06:50
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 zanarmstrong/ea803e0368721876e5442bbcd57440b3 to your computer and use it in GitHub Desktop.
Save zanarmstrong/ea803e0368721876e5442bbcd57440b3 to your computer and use it in GitHub Desktop.
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
}
p {
font-family: Courier;
}
#description {
position: absolute;
left: 500px;
top: 30px;
line-height: 6px
}
var canvas = d3.select("#map"),
width = canvas.property("width"),
height = canvas.property("height"),
context = canvas.node().getContext("2d");
var center = [width/2, height/2]
var missiles = [
{'name': 'Hwasong', 'dist': 625, 'tested': true, 'hidden': true},
{'name': 'Nodong', 'dist': 800, 'tested': true, 'hidden': true},
{'name': 'Musudan', 'dist': 2200, 'tested': true, 'hidden': true},
{'name': 'KN-08', 'dist': 7200, 'tested': false, 'hidden': true}
]
missiles.forEach(function(d){
d3.selectAll("."+ d.name).style("visibility", "hidden")
})
var updateDistance = function(distance){
document.getElementById("distance").innerHTML = "Distance from North Korea: " + distance + " miles"
}
var zoomScale = d3.scalePow().domain([0,1]).range([height * 4,height/6]).exponent(0.5)
var projection = d3.geoAzimuthalEquidistant()
.scale(zoomScale(0))
.translate(center)
.precision(0.1)
.rotate([-127.5101, -40.339, -20]);
var path = d3.geoPath()
.projection(projection)
.context(context);
var distBetweenPoints = function(a,b){
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2))
}
var drawCenteredCircle = function(radius, color, lineWidth){
context.beginPath();
context.arc(center[0], center[1], radius, 0, 2 * Math.PI);
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.lineWidth = 1;
}
/*
canvas.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged));
*/
var render = function() {},
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0; // Projection rotation as versor at start.
/*
function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
r0 = projection.rotate();
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(projection.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
projection.rotate(r1);
console.log(r1)
render();
}
*/
var toRadians = function(value) { return value * Math.PI / 180}
var toDegrees = function(value) { return value / Math.PI * 180}
LatLondistanceTo = function(a, b) {
var point1 = {
'lat': a[1],
'lon': a[0]
}
var point2 = {
'lat': b[1],
'lon': b[0]
}
var R = 3959;
var φ1 = toRadians(point1.lat), λ1 = toRadians(point1.lon);
var φ2 = toRadians(point2.lat), λ2 = toRadians(point2.lon);
var Δφ = φ2 - φ1;
var Δλ = λ2 - λ1;
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2)
+ Math.cos(φ1) * Math.cos(φ2)
* Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
};
LatLonGetPoint = function(distance, bearing, point) {
radius = 3959;
var δ = Number(distance) / radius; // angular distance in radians
var θ = toRadians(Number(bearing));
var φ1 = toRadians(point.lat);
var λ1 = toRadians(point.lon);
var sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1);
var sinδ = Math.sin(δ), cosδ = Math.cos(δ);
var sinθ = Math.sin(θ), cosθ = Math.cos(θ);
var sinφ2 = sinφ1*cosδ + cosφ1*sinδ*cosθ;
var φ2 = Math.asin(sinφ2);
var y = sinθ * sinδ * cosφ1;
var x = cosδ - sinφ1 * sinφ2;
var λ2 = λ1 + Math.atan2(y, x);
return {'lat': toDegrees(φ2), 'lon': (toDegrees(λ2)+540)%360-180}; // normalise to −180..+180°
};
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) {
if (error) throw error;
var nk = world.objects.countries.geometries.filter(function(d){return d.id == "408"})
var sphere = {type: "Sphere"},
land = topojson.feature(world, world.objects.land),
nktopo = topojson.feature(world, world.objects.countries);
var previous = 0;
var now = 0;
var updateBasedOnDistance = function(visibleDistance){
missiles.forEach(function(d){
if(visibleDistance > d.dist & d.hidden){
d.hidden = false;
d3.selectAll("." + d.name).style("visibility", "visible")
}
if(!d.hidden){
var point = LatLonGetPoint(d.dist, 0, {'lon': 127.51, 'lat': 40.3399});
drawCenteredCircle(distBetweenPoints(projection([point.lon, point.lat]), center), "#ffa296", 1)
}
})
drawCenteredCircle(center[0]* 1.2, "white", 120)
// update miles count
updateDistance(Math.round(visibleDistance /10)*10);
}
canvas.on("mousemove",function(){
var xy = d3.mouse(this);
console.log(projection.invert(xy))
console.log(LatLondistanceTo(projection.invert(xy), [127.51, 40.3399]))
})
render = function(stage) {
// update scale
projection.scale(zoomScale(stage))
// redraw world with new scale
context.clearRect(0, 0, width, height);
context.beginPath(), path(sphere), context.fillStyle = "#fff", context.fill();
context.beginPath(), path(nktopo), context.fillStyle = "#555", context.fill(), context.strokeStyle = "#eee", context.stroke();
context.beginPath(), path(sphere), context.stroke();
// calculate visible distance in image, and update accordingly
updateBasedOnDistance(LatLondistanceTo(projection.invert([width,center[1]]), [127.51, 40.3399]))
};
var stage = 0;
render(stage)
var t = d3.timer(function(elapsed) {
if (elapsed / 300 > stage) {
stage += .002;
render(stage)
}
if(stage > 1){
t.stop();
}
}, 500);
});
<!DOCTYPE html>
<meta charset="utf-8">
<link rel="stylesheet" href="baseStyles.css">
<body>
<div><canvas id="map" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 0;">
<canvas id="layerAnnotations" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 1;"></cavnas>
</div>
<div id="description"><p id="distance">Distance from North Korea: 300 miles</p>
<p>..............................................</p>
<p class="Hwasong">Scud Missile: ~625 miles reach</p>
<p class = "Hwasong"></p>
<p class="Nodong">Nodong Missile: ~800 miles reach</p>
<p class = "Nodong"></p>
<p class="Musudan">Musudan Missile: ~2200 miles reach</p>
<p class = "Musudan"></p>
<p class="KN-08">KN-08 Missile: ~7200 miles reach (in progress)</p>
<p class = "KN-08"></p></div>
<p style="position: absolute; left: 500px; top: 400px; z-index: 1;"><a href="http://www.nti.org/learn/countries/north-korea/">Source: Nuclear Threat Initiative</p>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@2"></script>
<script src="baseTemplate.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment