I was given the task of making a flight path like visualization in 3D using ThreeJS.

One of the requirements is that the city location info should be on a latitude-longitude system. Because it’s universal, precise and you can easily modify it. So it’s both dynamic and accurate.

The question is: How do you place a map like coordinate system on a 3D sphere? Once again our Math Masters explained it to us in no time. Longitude/Latitude is a polar coordinate system and to be able to place any given point on the sphere we need to translate those coordinates to the Cartesian System. More on coordinates system.

I’m not going to pretend I understand the whole thing, so I’m going to say what I understood from the solution:

var phiFrom = dataRecord.from.lat * Math.PI / 180; var thetaFrom = (dataRecord.from.lon + 90) * Math.PI / 180; //calculates "from" point var xF = radius * Math.cos(phiFrom) * Math.sin(thetaFrom); var yF = radius * Math.sin(phiFrom); var zF = radius * Math.cos(phiFrom) * Math.cos(thetaFrom);

By calculating how the angle from the coordinate points translate to an actual angle on our system and then multiplying it by the sphere’s radius and our beloved trigonometrical functions, you get the points on your 3D world. MathMagic.

Good enough. So, once you set up both “from” and “to” variables to get the points, it’ll be easier if you set vectors for them.

//Sets up vectors var vT = new THREE.Vector3(xT, yT, zT); var vF = new THREE.Vector3(xF, yF, zF);

Vectors are just points on the 3D world. Unless stated otherwise, their origin is at the scene’s origin [0,0,0].

Now, this should give you the starting and ending points for the path.

Now that we have the points set correctly on the globe, we should be able to make a nice path between them. I used Bezier Curves for the task, since it’s rather easy to use them. On ThreeJS, you can do cubic or quadratic bezier. A quadratic is built on three defined points, while the quadratic uses 4 control points. After trying quadratic, I decided to go with the cubic, because the shape of the paths are better defined with an extra control point doing it so.

So, you have your starting and ending points, but how do you set your middle control points?

There’s some things we need, but first let’s find the midpoint between the starting and ending points, as well as the distance between the first two points. It’s also a good idea to get started on the 2 control points by cloning the position of the starting ones.

// vF = vector From // vT vector To var dist = vF.distanceTo(vT); // here we are creating the control points for the first ones. // the 'c' in front stands for control. var cvT = vT.clone(); var cvF = vF.clone(); // then you get the half point of the vectors points. var xC = ( 0.5 * (vF.x + vT.x) ); var yC = ( 0.5 * (vF.y + vT.y) ); var zC = ( 0.5 * (vF.z + vT.z) ); // then we create a vector for the midpoints. var mid = new THREE.Vector3(xC, yC, zC);

Now we have the centre points of the paths.

Let’s now get in the math for establishing the points to draw the path once and for all.

var smoothDist = map(dist, 0, 10, 0, 15/dist ); mid.setLength( radius * smoothDist ); cvT.add(mid); cvF.add(mid); cvT.setLength( radius * smoothDist ); cvF.setLength( radius * smoothDist );

Here I am using a **map **function.

I learned about this when I was playing around with Arduino. What it does is quite simple and VERY usefeul. I got it from here and it works like this:

function map( x, in_min, in_max, out_min, out_max){ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }

You give it a number that (x) that is within a given range (in_min, in_max) and you want to transform that to a different range (out_min, out_max)

Here’s an example:

A review/sore system. It can go from 0-10 or 0-5 or 0-4 or whatever.

So, let’s say you give a movie 3 stars. How is that translatable to a 0-10 rating system?

In this case it’s easy, you just multiply by 2 and you get 6.

But what if it happens that the ranges are something like 0 to 345 and 40 to 160?

That’s when you use a map function to make your life easier.

Then we set the Length of the vector make it bigger than the sphere, relatively to the inverse of its distance, first to the midpoint. Then we add the midpoint to the vectors, to bring them closer together and then we set the length to the final vectors once more, to make them even bigger.

Finally, we create the path and add it to the scene.

var curve = new THREE.CubicBezierCurve3( vF, cvF, cvT, vT ); var geometry2 = new THREE.Geometry(); geometry2.vertices = curve.getPoints( 50 ); var material2 = new THREE.LineBasicMaterial( { color : 0xff0000 } ); // Create the final Object3d to add to the scene var curveObject = new THREE.Line( geometry2, material2 ); paths.push(curve); scene.add(curveObject);

To get a sphere following the path is rather easy. You just need to create a sphere on the path and then, on the render() function, you iterate through all the points of the path, one by frame, and update the sphere’s position to be this position.

Because I have multiple paths and spheres, I created arrays to contain them and control them individually.

for( var i = 0; i < movingGuys.length; i ++ ) { pt = paths[i].getPoint( t ); movingGuys[i].position.set( pt.x, pt.y, pt.z ); } t = (t >= 1) ? 0 : t += 0.002;

And this is how you make them move. Nicely enough, ThreeJS has a method .getPoint() that returns the position to a given point (from 0 to 1, like from 0% – 100%) as a vector and you can just set it to be the position of your object.

I hope you liked this post, feel free to comment and even help me, I guess this is not the best solution for setting up the control points.

Cheers!

So I was given this task: To find out which data visualization tool was better for the project. What it needed to comply with was: Responsiveness, good performance on cross-platform execution( should work flawlessly on mobile, desktop, tablets, fridges, etc.), interactivity, easiness of implementing and able to draw multiple charts and graphs styles. So, the first step: Research.

So, head to your favourite research engine and use all of your research skills. After a quick one(pun intended), a friend helped me find this nice table. It worked very nice as a first triage. Since some of our colleagues knew about D3.js and it showed to be pretty useful for us on the chart, that’s the one I started with.

So, D3 here we go. One of the hardest examples (as we’d planned thus far) to build was an interactive donut chart. By that, I mean a donut chart that you click on a slice and it slides along its normal. Let the image below explain it for me:

After some doodling around, I managed to build a donut chart. The code for it is a little messy at first, but simple too. You start by setting up your “canvas” size. Just remember that D3 works with SVG, not actual canvas. The thing about SVG is that because it’s **S**calable **V**ector **G**raphics, it can be scaled to any size you want with no distortion, unlike drawing pixels to the screen.

var width = 960, height = 700, outerRadius = Math.min(width*0.6, height*.6) * .5 - 10, innerRadius = outerRadius * .6; var n = 4, data0 = [30,20,40,10], data; var desc = ["Active", "Almost Active", "Low Actve", "Sedentary"]; var color = ["green", "yellow", "orange", "red"]; var arc = d3.svg.arc(); var pie = d3.layout.pie() .sort(null); var p; var toolTip = d3.select("body").append("div") .style("position","absolute") .style("padding","2px 10px") .style("background", "#cccbbb") .style("opacity",0.9) .style("display","block"); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); svg.selectAll(".arc") .data(arcs(data0)) .enter().append("g") .attr("class", "arc") .attr("toggle","false") .attr("x", width/2) .attr("y", height/2) .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") .attr("desc", function(d,i){return desc[i];}) .on("mouseover", function(d,i){ toolTip.transition() .duration(50) .style("display", "block"); toolTip.html(d3.select(this).attr("desc") + " : " + d.data + "%") .style("left", (d3.event.pageX + 5) + "px") .style("top", (d3.event.pageY + 5) + "px"); }) .on("mouseout", function(d,i){ toolTip.transition() .duration(50) .style("display", "none"); }) .append("path") .attr("fill", function(d, i) { return color[i]; }) .attr("d", arc) .append("text") .attr("text-anchor","middle") .text("äbcDEF"); function arcs(data0) { var arcs0 = pie(data0), i = -1, arc; while (++i < n) { arc = arcs0[i]; arc.innerRadius = innerRadius; arc.outerRadius = outerRadius; } return arcs0;

So this code is for simply creating the donut. 71 Lines to set up some stuff and generate the chart.

Now, for the interactivity part:

D3 offers a good deal of events and interaction. As you can see already, on the code above, it’s got some *mousein* and *mouseout* events, but that’s for starters. We need it to move. We need to Frankenstein it.

.on("click", function (d, i) { p = d3.select(this); console.log(this); d3.selectAll("g") .transition(200) .attr("transform","translate("+ (width/2) +","+(height/2)+")"); var cumulatedAngle = 0; for( var j = 0 ; j < (i) ; j++){ cumulatedAngle += chartValues[j]; } cumulatedAngle += 0.5 * chartValues[i]; var angle = ( (cumulatedAngle * 2.0 * Math.PI) / total ) var ang = angle - Math.PI * 0.5; var _x = Math.cos(ang) * 100; var _y = Math.sin(ang) * 100; if(p.attr("toggle") == "true"){ p.attr("toggle","false") }else{ d3.selectAll("g").attr("toggle","false"); p.attr("toggle","true"); p.transition() .duration(200) .attr("transform","translate("+ (_x + width/2) +","+(_y + height/2)+")"); } })

How did we do it? Pretty simple. We have an applied math masters in the team. That’s how. Well, I managed to get it started, but he’s the one who got it working in the end. Here we get the total values of the data, do some math to get the sine and cosine of the centre point of the slice and move it along its normal. This post is for comparing functionalities of the libraries, not how the code above works, if there’s any doubts on it, feel free to ask me in the comments.

What I want to highlight here is the lines that involve D3 code. See how it works to create the event listener, just like before, you do *.on(“identifier”, function(d, i ){})*. How you can select the clicked piece and, at the same time, all of the slices. D3 has got some very good control tool over the DOM elements. **D**ocument **O**bject **M**odel is, putting it simply, all of the elements on the screen.

This aspect of D3 is just what we need. It creates everything we need. Bar and line graphs are pretty straight forward, comparing to this. It’s got labels, interaction, the visual, everything. D3 is then a good candidate for our task.

There’s more. There’s this library called RaphaelJS. It also works with SVG, it’s got some interactive, it builds many kinds of graphs…. huh. Nice. Let’s take a look at it.

Raphael.fn.donutChart = function (cx, cy, r, rin, values, labels, stroke) { var paper = this, rad = Math.PI / 180, chart = this.set(); function sector(cx, cy, r, startAngle, endAngle, params) { //console.log(params.fill); var x1 = cx + r * Math.cos(-startAngle * rad), x2 = cx + r * Math.cos(-endAngle * rad), y1 = cy + r * Math.sin(-startAngle * rad), y2 = cy + r * Math.sin(-endAngle * rad), xx1 = cx + rin * Math.cos(-startAngle * rad), xx2 = cx + rin * Math.cos(-endAngle * rad), yy1 = cy + rin * Math.sin(-startAngle * rad), yy2 = cy + rin * Math.sin(-endAngle * rad); return paper.path(["M", xx1, yy1, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "L", xx2, yy2, "A", rin, rin, 0, +(endAngle - startAngle > 180), 1, xx1, yy1, "z"] ).attr(params); } var angle = 0, total = 0, start = 0, process = function (j) { var value = values[j], angleplus = 360 * value / total, popangle = angle + (angleplus / 2), color = Raphael.hsb(start, .75, 1), ms = 500, delta = 30, bcolor = Raphael.hsb(start, 1, 1), p = sector(cx, cy, r, angle, angle + angleplus, {fill: "90-" + bcolor + "-" + color, stroke: stroke, "stroke-width": 3}), txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad), cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j]).attr({fill: bcolor, stroke: "none", opacity: 0, "font-size": 20}); angle += angleplus; chart.push(p); chart.push(txt); start += .1; }; for (var i = 0, ii = values.length; i < ii; i++) { total += values[i]; } for (i = 0; i < ii; i++) { process(i); } return chart; }; var values = [], labels = []; $("tr").each(function () { values.push(parseInt($("td", this).text(), 10)); labels.push($("th", this).text()); }); $("table").hide(); Raphael("holder", 700, 700).donutChart(350, 350, 200, 50, values, labels, "#fff");

To begin with, Raphael doesn’t have direct support to create donut charts. You have to create that function inside Raphael, you sort of expand Raphael’s functionalities by creating a donutChart() function with the Raphael.fn method.

The thing about Raphael is, because it’s not data drive like D3, you have to set up more stuff. With D3, all you pretty much need to provide is the data, but with Raphael, you need to actually create the points for the vector drawing.

It’s a little more cumbersome and harder to understand, a lot more of variables and math involved. I got it from an online source, but I forgot to get the link to it. This code it’s not entirely mine.

p.click(function () { p.stop().animate({transform: "t100,100"}, ms, "elastic"); txt.stop().animate({opacity: 1}, ms, "elastic"); var i = this.id/2; var cumulatedAngle = 0; console.log(i); for( var j = 0 ; j < (i) ; j++){ console.log(values[j]); cumulatedAngle += values[j]; } cumulatedAngle += 0.5 * values[i]; var angle = ( (cumulatedAngle * 2.0 * Math.PI) / total ) var _x = Math.cos(angle) * 100; var _y = -Math.sin(angle) * 100; if(p.attr("toggle") == "true"){ p.attr("toggle","false") }else{ p.attr("toggle","true"); p.stop().animate({transform: "t"+_x+","+_y+""}, ms, "elastic"); } });

As for the interactivity part, it’s got the same principles, since math is math doesn’t matter the language. But see specially how the animation part works differently from D3. On Raphael I couldn’t get the slices to move back, I couldn’t find a way to work as intended, like D3 did.

A friend of mine, the same math masters, comes to me saying that he found out about this other library. It’s called **C3**. And guess what, it’s built on top of D3. It’s meant to be easier than D3 and have all of its functionality. I started off with the example C3 offers on the site, you can see it’s so simple that it gets to the point of being dumb.

var chart = c3.generate({ data: { columns: [ ['data1', 30], ['data2', 120], ], type : 'donut', onclick: function (d, i) { console.log("onclick", d, i); }, onmouseover: function (d, i) { console.log("onmouseover", d, i); }, onmouseout: function (d, i) { console.log("onmouseout", d, i); } }, donut: { title: "Iris Petal Width" } });

15 lines of code and you have your little donut chart. Pretty neat, eh? And try hovering the mouse over it. Clicking on the chart legend. So amazing. Isn’t it awesome? No.

Because it’s got so many built in functionalities, you have to take it all in. You can switch some toggles, deactivate some stuff, but you don’t have complete control over it.

This is my code for the solution:

var columnsNo = 4; var p; var h = 600; var w = 1000; var chartValues = [30, 25, 10, 22]; var total = 0; for(var i = 0; i < columnsNo; i++) total += chartValues[i]; var das; var chart = c3.generate({ data: { selection: { enabled: false }, labels: false, order: null, columns: [ ['Active', chartValues[0]], ['Almost Active', chartValues[1]], ['Not Very Active', chartValues[2]], ['Sedentary', chartValues[3]] ], type : 'donut', // onmouseover: function (d, i) {event.preventDefault(); return;}, // onmouseout: function (d, i) {event.preventDefault(); return;}, onclick: function (d, i) { p = d3.select(i); das = d3.selectAll("path"); for( var j = 0 ; j < columnsNo; j++ ){ if( d3.select(das[0][j]).attr("toggle") == "true" ){ d3.select(das[0][j]) .transition(100) .attr("transform","translate("+ (0) +","+(0)+")") .attr("toggle", "false"); } } var cumulatedAngle = 0; for( var j = 0 ; j < (d.index) ; j++){ cumulatedAngle += chartValues[j]; } cumulatedAngle += 0.5 * chartValues[d.index]; var angle = ( (cumulatedAngle * 2.0 * Math.PI) / total ) var ang = angle - Math.PI * 0.5; var _x = Math.cos(ang) * 100; var _y = Math.sin(ang) * 100; if(p.attr("toggle") == "true"){ p.attr("toggle","false") }else{ d3.selectAll("g").attr("toggle","false"); p.attr("toggle","true"); p.transition() .duration(100) .attr("transform","translate("+ (_x) +","+(_y)+")"); } }, }, tooltip: { show: true }, size: { height: h, width: w }, colors: { data1: 'green', data2: 'yellow', data3: 'orange', data4: 'red' }, donut: { label : { show:false }, title: "Active Population", width: 100, expand:false } });

See how I had to try and deactivate mouse functions manually. But that didn’t work. See how I had to think of a different way to get the pieces individually exploded. But you can still see how easy, fast and quick it is to set up, compared to the former 2 examples, and on the *mouseClick()* event I’m able of using D3’s selecting and manipulating functionalities to play with the DOMs.

So, with C3, I’m able to create charts very easily, pretty charts actually, I can apply the interactivity I need and….. it “works”. It “works” because it’s not entirely on my control. I believe that because of some of its built in events, I get this bug that if I move the mouse away while the animation of sliding, the slice freezes in place.

That’s something for later. I have to check some other stuff, work my way around and ask the forums, that kind of thing.

Raphael is more focused on design and art than data visualization, so it’s not as easy as D3 to accomplish this task. At least for now, let’s put Raphael aside. Raphael is nice, it is good, **but it’s not the tool for the job.** The important thing I’m trying to show you here is not all the code itself, but the fact that you can accomplish the same task in a million different ways. **There’s a lot of different tools for different tasks**, you should first **analyse and know what you want to do,** so you know what you are looking for on a tool and what you don’t need, for more than what you need can become an overkill and it might cause more bad than good. C3 has more than I need, but in a good way. For it is exactly what I need, it’s easy, responsive, interactive and will cut a lot of the monkey work. Although that bug is getting on the way, the trouble of finding a solution for it is worth the choice.