I am trying to use d3 to create a chart which will end up being almost like a bar chart. However, I would like to accomplish this by using very small individual squares. Something like the image below but ignoring the random squares dotted around:
http://i.imgur.com/jYSyhur.jpg
Each square represents a vote (or group of votes depending on how many votes are made on a given day) and can have 3 possible values, each with a different shade of blue, i.e. light blue, blue and dark blue.
One example I have already found is http://bost.ocks.org/mike/miserables/ but I would like to convert this style in to a bar chart.
I have already attempted at doing the markup in HTML using tables and divs, but this got widely out of control and ending up massively slowing down the page loading speed -- hence the use of SVG instead.
Any ideas on how to approach this would be appreciated.
The basis for what you want to do is the stacked bar chart: http://bl.ocks.org/mbostock/3886208
However, there are a few things you will need to take into account. The y and x axes must be proportional so you will have to carefully consider:
the size of the graph
the number of data points
the max value each data point can have
and select a height, width and "value" for each tick - each square.
I've made a demonstration fiddle here: http://jsfiddle.net/sa5RK/
It assumes a few things (for simplicity!):
Each tick is one value
The height & width of the box is set
var boxheight = 6;
the height / width can be dynamic based on the size of the box and data values
var margin = {top: 20, right: 20, bottom: 80, left: 40},
width = boxheight * data.length;
var max = d3.max(data, function(d){return d.a + d.b + d.c});
var height = max * boxheight;
I hope that's enough to get you started!
The other key thing which will help you out, is learning how each data joined selection can be set to a variable, then selecting inside of it will allow you access to the outside bound data. eg. below, each data group (value in the original array) -> has types (a,b or c) -> has rectangles
var groups = svg.selectAll(".group")
.data(data)
.enter().append("g")
.attr("transform", function(d,i){return "translate(" + x(i) + ", 0)"})
.attr("class", "group")
var types = groups.selectAll(".type")
.data(function(d){return d.offsets})
.enter().append("g")
.attr("transform", function(d){ return translate(0,y(d.y1))})
.attr("class", "type")
.attr("fill", function(d){return color(d.type)})
types.selectAll("rect")
.data(function(d){return d3.range(0,d.value)})
.enter().append("rect")
.attr("height", boxheight-0.5)
.attr("width", boxheight-0.5)
.attr("y", function(d){ return boxheight * d })
Related
djvanderlaan's d3.js Planetarium first defines a "sun" circle at the center of the SVG area:
svg.append("circle").attr("r", 20).attr("cx", w/2)
.attr("cy", h/2).attr("class", "sun")
and then defines two orbits around the sun (with code slightly rearranged for clarity here):
var planets = [
{ R: 300, r: 5, speed: 5, phi0: 90},
{ R: 150, r: 10, speed: 2, phi0: 190}
];
var container = svg.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
container.selectAll("g.planet").data(planets).enter().append("g")
.attr("class", "planet")
.each(function(d, i) {
d3.select(this).append("circle").attr("class", "orbit")
.attr("r", d.R)
d3.select(this).append("circle").attr("r", d.r).attr("cx",d.R)
.attr("cy", 0).attr("class", "planet");
});
The first circle in each group--the "orbit" circle--is never given center coordinates cx and cy. That's not just in the source code; I looked at the "orbit" circles in the inspectors in three browsers, and there is no cx or cy for the orbit circles. However, these circles are centered on the center of the SVG area, i.e. on x=w/2, y=h/2. How does the browser know where to place these circles? Does it inherit cx and cy from the enclosing g element? From the "sun"?
Yes, all svg elements inherit the transform and scale of their parent svg:g elements. You can use this to set a local center, as done here, or to play with scale and rotate with fine precision (since setting these all with the transform attribute can sometimes lead to unexpected results).
Often, people place their circle elements inside a parent g and position the g without ever setting cx/cy because a circle defaults to centering on the center of its parent. This isn't the case with svg:rect elements, which have to be offset to "center" them.
First I must apologize because I am a chemist and not a programmer, so I am venturing far into unknown territory.
I am working on developing some patterns for micro-contract printing to create templates for controlled cellular growth. The master cast for the template is printed on A4 transparency and regardless of how much space I use, the cost is more or less the same. One of my patterns is about 2 x 2 mm, so you can imagine how many I can fit on the master template.
With that in mind what I would like to do is generate a repeating array of circles and tracks. This is easily accomplished in adobe illustrator, but it has become tedious. I would like to automate the ability to vary the dimensions of the circles, the width of the tracks connecting them, and the spaces between the circles.
For example, I might want a 20 x 20 grid of 30 um circles connected with a 10 um wide track with circles that are 150 um between the edges.
I was hoping to do this in Matlab, because I'm currently learning Matlab for some image processing capabilities.
An example of what the final product looks like can be seen:
http://www.nature.com/srep/2014/140424/srep04784/full/srep04784.html
http://pubs.rsc.org/en/Content/ArticleLanding/2011/LC/c1lc20257j#!divAbstract
I would appreciate some direction in:
Is doing this in Matlab even a good idea to begin with?
Setting up a code to generate a "grid" of circles
Connecting those circles with vertical, horizontal or diagonal tracks
I feel like this something someone has done before, so even pointing me to similar code that I could study would be a lot of help
Thanks!
I'm not very used to Matlab so I can't tell you for 0).
Below is a possible answer for 1) and 3). If you think my answer can help you, I can write some code about 2).
The library d3.js might be of interest for what you're doing. It basically allows you to bind data to svg elements.
Here is an example of what you could do. Let's say your data is a list of circle properties (size, position)
JSFiddle here
data = [ {x: 20µm, y:250µm, radius: 250µm}, {....}, ... ]
//Dimensions of what will be dsplayed in your browser
width = 500px
height = 700px
//d3.js provides functions to automatically resize your data to the viewport (maps domain -> range)
xScale = d3.scale.linear()
.domain([0, a4_format_size_x])
.range([0, width])
yScale = d3.scale.linear()
.domain([0, a4_format_size_y])
.range([0, height])
rScale = d3.scale.linear()
.domain([0, max_circle_size])
.range([0, 20])
svg = d3.select(element[0])
.append("svg")
.attr("width", width)
.attr("height", height)
svg.selectAll("circle")
.data(data) // This line binds your date to SVG elements
.enter()
.append("circle")
.attr("cx", function(data) { return xScale(data.x)})
.attr("cy", function(data) { return yScale(data.y)})
.attr("r", function(data) { return rScale(data.radius)}
Note : the syntax selectAll > enter > append might seem weird at first, if you're puzzled feel free to have a look at this
Now for the generation of the data itself (a "grid of circles"), you could have something like
var numCirclesX = 500
var numCirclesY = 700
var data = []
for(var i=0; i<numCirclesX; i++){
for(var j=0; j<numCirclesY, j++){
data.push({ x: i*size_A4_x/numCirclesX,
y: j*size_A4_y/numCirclesY,
radius: 5 })
}
}
I'm trying to zoomable/draggle rectangle from going outside of the svg bounds when panning and zooming. I've tried to implement it based off of this example, but i cant seem to get it to work. I've created this jsfiddle with just the rectangle that is zoomable and draggable. Again, im trying to make it so that you can not drag the rectangle outside of the svg box i put the border on. I know i need to update the move function. the code below is from the first link example but it does not seem to work well so i commented part of it out.
function move() {
var t = d3.event.translate,
s = d3.event.scale;
//t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0]));
//t[1] = Math.min(height / 2 * (s - 1) + 230 * s, Math.max(height / 2 * (1 - s) - 230 * s, t[1]));
//zoom.translate(t);
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Edit: So additionally i need to be able to drag the rectangle when you are zoomed in all the way and its bigger than the svg. In the image below, the blue rectangle is the svg and green would be the rectangle and you are zoomed in all the way so that the green rectangle takes up the much more than the SVG. This is similar to the map in the constrained zoom example. You can zoom into the states and drag across the country, navigating to states outside the current svg size
You can do this by constraining the translation coordinates you set to the size of the box:
var t = d3.event.translate,
s = d3.event.scale;
t[0] = Math.max(0, Math.min(t[0], width - s*50));
t[1] = Math.max(0, Math.min(t[1], height - s*50));
svg.attr("transform", "translate(" + t + ")scale(" + d3.event.scale + ")");
This is constraining the x coordinate to be between 0 and the width minus however much space is required to show the box completely -- this depends on the zoom level and the term therefore contains s. For the y coordinate, it is exactly the same.
This is much easier if you don't use both a translation and explicit coordinate settings through x and y for the box -- to offset from the top left corner, simply set an initial translation.
Complete example here.
For a project we are trying to make a circle into a line (and back again) while it is rotating along a linear path, much like a tire rotates and translates when rolling on a road, or a curled fore finger is extended and recurled into the palm.
In this Fiddle, I have a static SVG (the top circle) that rotates along the linear black path (which is above the circle, to mimic a finger extending) that is defined in the HTML.
I also use d3 to generate a "circle" that is made up of connected points (and can unfurl if you click on/in the circle thanks to #ChrisJamesC here ), and is translated and rotated
in the function moveAlongLine when you click on the purple Line:
function moveAlongLine() {
circle.data([lineData])
.attr("transform", "translate(78.5,0) rotate(-90, 257.08 70) ")
.duration(1000)
circle.on("click", transitionToCircle)
}
The first problem is that the .duration(1000) is not recognized and throws a Uncaught TypeError: Object [object Array] has no method 'duration' in the console, so there is a difference between the static definition of dur in SVG and dynamically setting it in JS/D3, but this is minor.
The other is should the transform attributes be abstracted from one another like in the static circle? in the static circle, the translate is one animation, and the rotation is another, they just have the same star and duration, so they animate together. How would you apply both in d3?
The challenge that I can not get, is how to let it unroll upwards(and also re-roll back), with the static point being the top center of the circle also being the same as the leftmost point on the line.
these seem better:
I should try to get the unfurl animation to occur while also rotating? This seems like it would need to be stepwise/sequential based...
Or Consider an octogon (defined as a path), and if it were to rotate 7 of the sides, then 6, then 5.... Do this for a rather large number of points on a polyhedron? (the circle only needs to be around 50 or so pixels, so 100 points would be more than enough) This is the middle example in the fiddle. Maybe doing this programmatically?
Or This makes me think of a different way: (in the case of the octogon), I could have 8 line paths (with no Z, just an additional closing point), and transition between them? Like this fiddle
Or anything todo with keyframes? I have made an animation in Synfig, but am unsure ho get it to SVG. The synfig file is at http://specialorange.org/filedrop/unroll.sifz if you can convert to SVG, but the xsl file here doesn't correctly convert it for me using xsltproc.
this seems really complicated but potential:
Define a path (likely a bézier curve with the same number of reference points) that the points follow, and have the reference points dynamically translate as well... see this for an concept example
this seems complicated and clunky:
Make a real circle roll by, with a growing mask in front of it, all while a line grows in length
A couple of notes:
The number of points in the d3 circle can be adjusted in the JS, it is currently set low so that you can see a bit of a point in the rendering to verify the rotation has occurred (much like the gradient is in the top circle).
this is to help students learn what is conserved between a number line and a circle, specifically to help learn fractions. For concept application, take a look at compthink.cs.vt.edu:3000 to see our prototype, and this will help with switching representations, to help you get a better idea...
I ended up using the same function that generates the circle as in the question, and did a bit of thinking, and it seemed like I wanted an animation that looked like a finger unrolling like this fiddle. This lead me to the math and idea needed to make it happen in this fiddle.
The answer is an array of arrays, with each nested array being a line in the different state, and then animate by interpolating between the points.
var circleStates = [];
for (i=0; i<totalPoints; i++){
//circle portion
var circleState = $.map(Array(numberOfPoints), function (d, j) {
var x = marginleft + radius + lineDivision*i + radius * Math.sin(2 * j * Math.PI / (numberOfPoints - 1));
var y = margintop + radius - radius * Math.cos(2 * j * Math.PI / (numberOfPoints - 1));
return { x: x, y: y};
})
circleState.splice(numberOfPoints-i);
//line portion
var lineState = $.map(Array(numberOfPoints), function (d, j) {
var x = marginleft + radius + lineDivision*j;
var y = margintop;
return { x: x, y: y};
})
lineState.splice(i);
//together
var individualState = lineState.concat(circleState);
circleStates.push(individualState);
}
and the animation(s)
function all() {
for(i=0; i<numberOfPoints; i++){
circle.data([circleStates[i]])
.transition()
.delay(dur*i)
.duration(dur)
.ease("linear")
.attr('d', pathFunction)
}
}
function reverse() {
for(i=0; i<numberOfPoints; i++){
circle.data([circleStates[numberOfPoints-1-i]])
.transition()
.delay(dur*i)
.duration(dur)
.ease("linear")
.attr('d', pathFunction)
}
}
(Note: This should be in comments but not enough spacing)
Circle Animation
Try the radial wipe from SO. Need to tweak it so angle starts at 180 and ends back at same place (line#4-6,19) and move along the X-axis (line#11) on each interation. Change the <path... attribute to suit your taste.
Line Animation Grow a line from single point to the length (perimeter) of the circle.
Sync both animation so that it appears good on all browsers (major headache!).
I'm drawing bar graphs on time scale x-axis.
For example,
var x = d3.time.scale()
.domain([minDate, maxDate])
.range([0, width]);
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", bar_graph.x())
.attr("y", d.value)
.attr("width", 10);
The left corner of the rect starts at the assigned x-point, but I want to assign the x-point to the center of the rect.
Since it's time scale, simple substraction x-5 (assuming the width is 10px) didn't work. I tried invert(), also checked svg reference, but fail.
The problem is indeed in setting the x attribute, it appears the issue lies in the bar_graph.x() statement, try doing something like this:
.attr("x", function(d) {return x(d.dateVal)-barwidth/2;})
See a full implementation here: http://jsfiddle.net/qAHC2/4/