Rotate the globe by dragging it or by adjusting the sliders. When you rotate, you are adjusting the Orthographic projection's three Euler angles.
See also:
The world countries polygons were downloaded from ArcGIS.
xxxxxxxxxx
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
}
.point-mouse {
fill: steelblue;
}
#rotation {
position: absolute;
font-family: monospace;
padding: 10px;
background: rgba(255, 255, 255, .5);
}
#rotation input {
width: 300px;
}
</style>
</head>
<body>
<div id="rotation"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson@3.0.2/dist/topojson.js"></script>
<script src="https://unpkg.com/versor@0.0.3/build/versor.min.js"></script>
<script>
var angles = ["λ", "φ", "γ"];
angles.forEach(function(angle, index){
d3.select("#rotation").append("div")
.attr("class", "angle-label angle-label-" + index)
.html(angle + ": <span>0</span>")
d3.select("#rotation").append("input")
.attr("type", "range")
.attr("class", "angle angle-" + index)
.attr("min", "-180")
.attr("max", "180")
.attr("step", "1")
.attr("value", "0");
});
var width = window.innerWidth, height = window.innerHeight;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection = d3.geoOrthographic()
.scale(d3.min([width / 2, height / 2]))
.translate([width / 2, height / 2])
.precision(1);
var path = d3.geoPath()
.projection(projection);
var graticule = d3.geoGraticule()
.step([10, 10]);
var 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.
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path)
.style("fill", "none")
.style("stroke", "#ccc");
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
svg.call(drag);
function dragstarted(){
var mouse_pos = d3.mouse(this);
v0 = versor.cartesian(projection.invert(mouse_pos));
r0 = projection.rotate();
q0 = versor(r0);
svg.insert("path")
.datum({type: "Point", coordinates: projection.invert(mouse_pos)})
.attr("class", "point point-mouse")
.attr("d", path);
}
function dragged(){
var mouse_pos = d3.mouse(this);
var v1 = versor.cartesian(projection.rotate(r0).invert(mouse_pos)),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
if (r1){
update(r1);
svg.selectAll("path").attr("d", path);
svg.selectAll(".point-mouse")
.datum({type: "Point", coordinates: projection.invert(mouse_pos)});
}
}
function dragended(){
svg.selectAll(".point").remove();
}
d3.selectAll("input").on("input", function(){
// get all values
var nums = [];
d3.selectAll("input").each(function(d, i){
nums.push(+d3.select(this).property("value"));
});
update(nums);
svg.selectAll("path").attr("d", path);
});
function update(eulerAngles){
angles.forEach(function(angle, index){
d3.select(".angle-label-" + index + " span").html(Math.round(eulerAngles[index]))
d3.select(".angle-" + index).property("value", eulerAngles[index])
});
projection.rotate(eulerAngles);
}
d3.json("countries.json", function (error, countries){
if (error) throw error;
// JOIN
svg.selectAll(".subunit")
.data(topojson.feature(countries, countries.objects.polygons).features)
.enter().append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1px")
})
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://unpkg.com/topojson@3.0.2/dist/topojson.js
https://unpkg.com/versor@0.0.3/build/versor.min.js