Last active
May 16, 2017 06:50
-
-
Save zanarmstrong/ea803e0368721876e5442bbcd57440b3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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 | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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