Work in progress, trying to understand the relationship between cartesian and spherical coordinates.
Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="trackball.js"></script>
<script src="d3.parcoords.js"></script>
<link rel="stylesheet" type="text/css" href="d3.parcoords.css"></link>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg {
width: 100%; height: 300px;
float: left;
}
.parcoords {
width: 100%; height: 200px;
float:left; clear: left;
}
path.foreground {
fill: none;
stroke: #333;
stroke-width: 1.5px;
}
path.graticule {
fill: none;
stroke: #aaa;
stroke-width: .5px;
}
</style>
</head>
<body>
<svg></svg>
<div class="parcoords"></div>
<script>
var zero = 1e-6;
var normals = [
{ x: 0, y: 0, z: 1, i: 0 },
{ x: 0, y: 1, z: 0, i: 1 },
{ x: 1, y: 0, z: 0, i: 2 },
]
// Parallel coordinates
var colorScale = d3.scale.category10();
var colorScale = d3.scale.ordinal()
.range(["#f00", "#0f0", "#00f"])
function color(d) {
return colorScale(d.i);
}
var pc = d3.parcoords()(".parcoords")
.dimensions(['x', 'y', 'z'])
.data([
{x: -1, y: -1, z: -1},
{x: 1, y: 1, z: 1}
])
.autoscale()
.color(color)
.alpha(0.8)
.createAxes()
.data(normals)
.render();
// "Globe" -> Unit sphere
var map_width = 300;
var map_height = 300;
var scale = (map_width - 1) / 2 / Math.PI * 2.5
var projection = d3.geo.orthographic()
.translate([map_width/2, map_height / 2])
.scale(scale)
.rotate([0,0,0])
.clipAngle(90)
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("svg")
var left = svg.append("g")
.attr("transform", "translate(100, 0)")
left.selectAll("line.normal")
.data(normals)
.enter().append("line").classed("normal", true)
.attr({
stroke: color,
x1: map_width/2,
y1: map_height/2,
x2: getX,
y2: getY
})
left.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
left.selectAll("circle.normal")
.data(normals)
.enter().append("circle").classed("normal", true)
.attr({
r: 5,
fill: color,
cx: getX,
cy: getY
})
d3.behavior.trackball(svg).on("rotate", function(rot) {
//update the rotation in our projection
projection.rotate(rot);
//redraw our visualization with the updated projection
left.selectAll("path.graticule")
.attr("d", path)
left.selectAll("circle.normal")
.attr({
cx: getX,
cy: getY
})
left.selectAll("line.normal")
.attr({
x2: getX,
y2: getY
})
//update the parallel coordinates
var rotated = normals.map(function(n) {
var ll = unit2latlon(n);
ll[0] += rot[0];
ll[1] += rot[1];
var r = latlon2unit(ll)
r.i = n.i;
return r;
})
pc.data(rotated)
.render();
})
function getX(d) {
var ll = unit2latlon(d);
return projection(ll)[0]
}
function getY(d) {
var ll = unit2latlon(d);
return projection(ll)[1]
}
// convert our unit vectors into lat/lon
function unit2latlon(v) {
//https://stackoverflow.com/questions/5674149/3d-coordinates-on-a-sphere-to-latitude-and-longitude
var r = 1; //Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
//var theta = Math.acos(v.z/r);
//var phi = Math.atan(v.x/(v.y ? v.y : zero));
// we switch the given formulation with z -> x and y <-> z
// so that our axes match the traditional x is right, y is up and z is out
var theta = Math.acos(v.x/r);
var phi = Math.atan(v.y/(v.z ? v.z : zero));
// lat, lon
return [90 - rad2deg(theta), rad2deg(phi)]
}
function rad2deg(r) {
return r * 180/Math.PI;
}
function deg2rad(d) {
return d * Math.PI/180;
}
function latlon2unit(latlon) {
var lat = latlon[0];
var lon = latlon[1];
if(!lat) lat = zero;
if(!lon) lon = zero;
lat = deg2rad(lat);
lon = deg2rad(lon);
// we switch the given formulation with z -> x and y <-> z
var z = Math.cos(lat) * Math.cos(lon);
var y = Math.cos(lat) * Math.sin(lon);
var x = Math.sin(lat)
return {x: x, y:y, z: z}
}
// on rotate
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js