Dynamic SVG Coordinate Space
The Goal
In this section, we will cover how to make the SVG Coordinate Space dynamic so that our Data Visualization is visible regardless of the data.
We will the make the SVG Coordinate Space scale up and/or down to fit our data.
Three SVG Rectangle Example
We start with three rectangles
(similar to our example in the Using JSON to Simplify Code section).
1var jsonRectangles = [ 2 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 3 { "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 4 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }]; 5 6var svgContainer = d3.select("body").append("svg") 7 .attr("width", 100) 8 .attr("height", 100); 9 10var rectangles = svgContainer.selectAll("rect") 11 .data(jsonRectangles) 12 .enter() 13 .append("rect"); 14 15var rectangleAttributes = rectangles 16 .attr("x", function (d) { return d.x_axis; }) 17 .attr("y", function (d) { return d.y_axis; }) 18 .attr("height", function (d) { return d.height; }) 19 .attr("width", function (d) { return d.width; }) 20 .style("fill", function(d) { return d.color; });
Which gives us:
Which is great.
The SVG Viewport (container)
1var svgContainer = d3.select("body").append("svg") 2 .attr("width", 100) 3 .attr("height", 100);
has a width of 100 units and a height of 100 units.
Which means that the lower right most point of the red rectangle lands at (90,90), which is still inside of our view port.
What if our purple rectangle x-coordinate, suddenly quadrupled from 40 to 160?
1//Going from 2{ "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" } 3 4//to 5{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" } 6 7//so that our jsonRectangles becomes 8var jsonRectangles = [ 9{ "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 10{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 11{ "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];
This would mean that the purple rectangle would have x,y coordinates of (160,40).
This coordinate is out of our viewport which has a width of 100 units and a height of 100 units.
Suddenly our Data Visualization would look like this:
As you can imagine, This is not good!.
Manually Adjusting SVG Container Space
To get our new data to fit inside of our SVG container, we would have to increase the width of the container to accommodate the new x-coordinate for the purple triangle.
Since the new purple rectangle is defined as follows:
1{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }
It will have coordinates (160,40) at the top left and (180,60) at the bottom right.
So we could just re-write the SVG Viewport (container) to have a width that was much bigger than 180, say 200...
1var svgContainer = d3.select("body").append("svg") 2 .attr("width", 200) 3 .attr("height", 100);
Suddenly our Data Visualization would look like this:
Which is great, until the x-coordinate increases again. Or, the y-coordinate increases, or ... etc.
Dynamically Adjusting SVG Container Space
As you can guess, what we really want to do is to dynamically change the width and height attributes of the SVG Container/Viewport according to our data.
We are going to use some basic JavaScript to loop through our array of JSON objects to get the max x-coordinate and the max y-coordinate.
The max x-coordinate and max y-coordinate will be the bottom right hand point of the rectangle.
1//New jsonRectangles Data (with purple rectangle x-coordinate now 160) 2var jsonRectangles = [ 3 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 4 { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 5 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }]; 6 7var max_x = 0; //This will be updated to be the max x-coordinate 8var max_y = 0; //This will be updated to be the max y-coordinate 9 10//We loop through our jsonRectangles array 11for (var i = 0; i < jsonRectangles.length; i++) { 12 13 var temp_x, temp_y; 14 15 // To get the farthest right hand point, we need to add the x-coordinate and the width 16 var temp_x = jsonRectangles[i].x_axis + jsonRectangles[i].width; 17 18 // To get the farthest bottom point, we need to add the y-coordinate and the height 19 var temp_y = jsonRectangles[i].y_axis + jsonRectangles[i].height; 20 21 /** 22 * If the temporary x-coordinate is bigger than the max_x, 23 * make the max_x equal to the temp_x 24 * otherwise, do nothing. 25 */ 26 if ( temp_x >= max_x ) { 27 max_x = temp_x; 28 } 29 30 /** 31 * If the temporary y-coordinate is bigger than the max_y, 32 * make the max_y equal to the temp_y 33 * otherwise, do nothing. 34 */ 35 if ( temp_y >= max_y ) { 36 max_y = temp_y; 37 } 38 39}//End of the loop 40 41max_x; 42//returns 180 43 44max_y; 45//returns 90
If you run this in the JavaScript Console, you will get that the max_x is 180 and the max_y is 90.
If the data changes, this max_x and max_y will always have maximum values of our data.
Now we have to update our SVG Container Viewport:
1//From 2var svgContainer = d3.select("body").append("svg") 3 .attr("width", 200) 4 .attr("height", 100); 5 6//to (using the new max_x and max_y variables) 7var svgContainer = d3.select("body").append("svg") 8 .attr("width", max_x + 20) 9 .attr("height", max_y + 20); 10//Note - we add 20 units to the max_x and max_y to give the elements some stylistic room
Notice the note - we add some space to the max_x and max_y to give the elements some stylistic room.
In this way, our SVG Container will now always display the right dimensions so that the data fits correctly inside of it.
The Finished Product
Now that we've figured that out, the full code with the updated jsonRectangles data now reads (javascript comments removed):
1var jsonRectangles = [ 2 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 3 { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 4 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }]; 5 6var max_x = 0; 7var max_y = 0; 8 9for (var i = 0; i < jsonRectangles.length; i++) { 10 var temp_x, temp_y; 11 var temp_x = jsonRectangles[i].x_axis + jsonRectangles[i].width; 12 var temp_y = jsonRectangles[i].y_axis + jsonRectangles[i].height; 13 14 if ( temp_x >= max_x ) { max_x = temp_x; } 15 16 if ( temp_y >= max_y ) { max_y = temp_y; } 17} 18 19var svgContainer = d3.select("body").append("svg") 20 .attr("width", max_x) 21 .attr("height", max_y) 22 23var rectangles = svgContainer.selectAll("rect") 24 .data(jsonRectangles) 25 .enter() 26 .append("rect"); 27 28var rectangleAttributes = rectangles 29 .attr("x", function (d) { return d.x_axis; }) 30 .attr("y", function (d) { return d.y_axis; }) 31 .attr("height", function (d) { return d.height; }) 32 .attr("width", function (d) { return d.width; }) 33 .style("fill", function(d) { return d.color; });
Which gives us:
There we go!
All of the rectangles are there.
The SVG Viewport has the right dimensions (max_x + 20, max_y + 20).
And the SVG Viewport was generated dynamically so we didn't have to manually update the width and height.
Using a JavaScript FOR loop we were able to dynamically resize our SVG Viewport Container to fit our data.
So if/when the data changes again, this viewport will be ready to contain all of the Data Visualization.
If you found this D3 Tutorial helpful, you'll enjoy these FREE videos: