I am struggling to keep the area graph inbounds for a particular set of data. I am not able to figure out what exactly is making it go out of range
var xRange = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([0, numberOfDays + 1]),
yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([_.min(areaData), _.max(areaData)]);
js fiddle here
https://jsfiddle.net/sahils/o7df3tyn/20/
Its due to you setting them exactly so these lines :
<svg id="visualisation" width="1200" height="400"></svg>
And
WIDTH = 1000,
HEIGHT = 400,
Rather than this just use window size :
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
vis = d3.select('#visualisation').attr('width', WIDTH).attr('height', HEIGHT)
And remove the styling from your html. Updated fiddle : https://jsfiddle.net/thatOneGuy/o7df3tyn/23/
Related
With d3.js I have created d3 dendrograms to visualize hierachicals relations between objects. Dimensions and margins of the graph are defined with fixed height and width values.
var width = 1000,
height = 800,
boxWidth = 150,
boxHeight = 35,
gap = {
width: 50,
height: 12
},
margin = {
top: 16,
right: 16,
bottom: 16,
left: 16
},
svg;
With a few relations, display is ok but with many relations it's doesn't fit, graph is 'cut' and I can't see the entire graph. How to set this width and height properties dynamically and adjusted to the size of the graph ?
An example with a correct display : Codepen
An example with an incorrect display : Codepen
Let's work this out, you need to know the bounding box of your content first and then adjust the svg size. To do that, in this particular case, you only have to look at the boxes or nodes and can ignore the links.
With that in mind you can do the following after populating the Nodes in your renderRelationshipGraph function and return the calculated value:
function renderRelationshipGraph(data) {
// ...
var bbox = Nodes.reduce(function (max, d)
{
var w = d.x + boxWidth;
var h = d.y + boxHeight;
if (w > max[0]) {max[0] = w}
if (h > max[1]) {max[1] = h}
return max
}, [0,0])
return bbox
}
then on the main code change use it to update height and width of the svg:
svg = d3.select("#tree").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("g");
var bbox = renderRelationshipGraph(data);
svg.attr("width", bbox[0])
.attr("height", bbox[1]);
You can add a transition and limit the height but this does what you requested with a really large end result.
I'm trying to create a bar chart to visualize the number of Data-Science jerbs around the Federal Republic of Germany- right now it looks like this:
It's getting there, but it's not legible, which is a problem.
I want it to render in such a way that it will display the data in a comprehensible way no matter the size of the input file, i.e. it should be dynamic.
I know that data.length is the number of rows- but- what is meant by "rows", the number of lines in my input csv?
I've been recommended to use something like chart.setBounds(100, 100, data.length * k, 300) where k should be the height of the label + some margin. What is the best way to determine k?
I suppose that k is related to the y-axis and that really it is, or should be, just set by the maximum value of the inputs and that there isn't really much else I can do about that. Is it so?
I've been playing around with trying different values heuristically, i.e. trial and error- but that is clearly suboptimal.
What's a maintainable and effective way of always generating a map where the indices of the x-axis are all clearly readable and the y-axis is determined by the max value of inputs?
Here is the code I'm using to generate the bar chart.
<!DOCTYPE html>
<html>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://dimplejs.org/dist/dimple.v1.1.1.min.js"></script>
<script type="text/javascript">
function draw(data) {
"use strict";
var margin = 75,
width = 9000 - margin,
height = 600 - margin;
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)
.attr("height", height + margin)
.append('g')
.attr('class','chart');
var chart = new dimple.chart(svg,data);
chart.setBounds(100, 100, 500, 300);
var x = chart.addCategoryAxis("x", '"loc"');
var y = chart.addMeasureAxis("y", '"title"');
var lines = chart.addSeries(["project"], dimple.bar, [x, y]);
chart.draw();
};
</script>
<body>
<script type="text/javascript">
d3.csv("Germany.csv", draw);
</script>
</body>
</html>
(This is a link to the data file Germany.csv on my GitHub).
Firstly I am iterating over the data that you have provided to remove all double quotes in the key and value of the json like this.
datas = [];
data.forEach(function(d) {
var ob = {};
for (var key in d) {
var k = key.replace(/"/g, "").trim();//remove all double quotes and trim
var v = d[key].replace(/"/g, "").trim();//remove all double quotes and trim
ob[k]=v;
}
datas.push(ob)
})
Then I make the width of the svg based on the data length.
var width = data.length*5 -margin;//5 is a constant width of the text label font
if (width < 500)//set the minimum width in case data set is low.
width =600;
Set the width of svg an d chart like this
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)//set the calcuated width to svg
//set width to the chart object
chart.setBounds(100, 100, width -margin, 300);
working code 110 points here
working code with all points here
My goal is to make a bar chart that demonstrates the number of jobs advertised in particular locations.
I'm using this code, it draws on d3 and also dimple:
<script type="text/javascript">
function draw(data) {
/*
D3.js setup code
*/
"use strict";
var margin = 75,
width = 1400 - margin,
height = 600 - margin;
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)
.attr("height", height + margin)
.append('g')
.attr('class','chart');
/*
Dimple.js Chart construction code
*/
var myChart = new dimple.chart(svg, data);
myChart.addCategoryAxis("x", "Location");
myChart.addMeasureAxis("y", "Jobs");
myChart.addSeries(null, dimple.plot.bar);
myChart.draw();
};
</script>
It more or less works, but the thing is- the result is pretty useless because the X axis is so crowded that each individual location is essentially invisible.
Is there a way to enforce some reasonable amount of spacing there so that the different locations remain legible in such a way that it can withstand more records being added at a later date- so- with some kind of dynamism.
Ok, so- on the advice of #thisOneGuy I started playing around with increasing the width, and it worked.
At first I tried to increase the width too much and the chart just disappeared (if anyone knows why that happened I would be interested to hear about it in the comments perhaps)
from width = 1400 - margin, to width = 14000 - margin, it disappears
but width = 9000 - margin, was ok.
you can find the result here
I have a geo map. Everything is running just fine but the the map that is drawn is incredibly tiny. I have checked the GEOJSON for errors and it works fine. In the JS Box there is a proper working Demo that is commented out to see a working example.
How Do I get my map to Scale up to fill my svg?
http://codepen.io/MichaelArledge/pen/VeeVmY?editors=011
$.getJSON("https://googledrive.com/host/0B9jw0MX1C_D_N1plZFhjTlZwY3c", function(json){
var max = community_per_capita_totals.top(1)[0].value;
// create a first guess for the projection
var center = d3.geo.centroid(json)
var scale = 100;
var offset = [width/2, height/2];
var projection = d3.geo.mercator().scale(scale).center(center).translate(offset);
// create the path
var path = d3.geo.path().projection(projection);
// using the path determine the bounds of the current map and use
// these to determine better values for the scale and translation
var bounds = path.bounds(json);
var hscale = scale*width / (bounds[1][0] - bounds[0][0]);
var vscale = scale*height / (bounds[1][1] - bounds[0][1]);
var scale = (hscale < vscale) ? hscale : vscale;
var offset = [width - (bounds[0][0] + bounds[1][0])/2, height - (bounds[0][1] + bounds[1][1])/2];
// new projection
projection = d3.geo.mercator().center(center).scale(scale).translate(offset);
path = path.projection(projection);
chart.dimension(community_dim)
.group(community_per_capita_totals)
.width(width)
.height(height)
.colors(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"])
.colorDomain([0, max])
.projection(d3.geo.mercator()
.center(center)
.scale(scale)
.translate(offset))
.overlayGeoJson(json["features"], "Community")
dc.renderAll();
});
The issue is with your logic for determining the projection.scale(). I am not sure how to modify your logic to give you a custom scale for your map, but here is an example of scale logic I have used in maps before. The factors I am multiplying width and height by are based on the overall aspect ratio of the albersUsa projection, so you would need to tweak those to get a good fit for the mercator projection.
var scale = Math.min(width * 1.2, height * 2.1);
var projection = albersUsaPr()
.scale(scale)
.translate([width / 2, height / 2]);
i'm trying to somehow sweep in a half-donut-chart, meaning starting with a blank screen the chart starts drawing at -90 degree (or 270) and performs a halfcircle until reaching 90 degree. the code looks like:
var width = 800;
var height = 400;
var radius = 300;
var grad=Math.PI/180;
var data = [30, 14, 4, 4, 5];
var color = d3.scale.category20();
var svg = d3.select("body").append("svg").attr("width", width).attr("height",
`height).append("g").attr("transform", "translate(" + radius*1.5 + "," + radius*1.5 +
")");
var arc = d3.svg.arc().innerRadius(radius - 100).outerRadius(radius - 20);
var pie = d3.layout.pie().sort(null);
svg.selectAll("path").data(pie(data)).enter().append("path").attr("d",
arc).attr("fill",
function(d, i) { return color(i); }).transition().duration(500).attrTween("d", sweep);
function sweep(a) {
var i = d3.interpolate({startAngle: -90*grad, endAngle: -90*grad},{startAngle: -90*grad, endAngle: 90*grad});
return function(t) {
return arc(i(t));
};
}
looking at several examples i managed to get the animation, however, i fail at binding (or converting) the data to the arc. my feeling is that there is only one path drawn and then it stops.
if i change the interpolation to start/end -90/90 and a, i get different colors but not all of them. adding the start/end-angle to the pie-var gives me a transition where a one-colored-arc is shown at the beginning and then the other parts slide in (which would be correct if there was no arc at the beginning - the proportions also seem a bit wrong). setting the initial color to white does not help because then everything stays white.
i'm afraid i'm missing an obvious point, but so far i'm stuck, maybe someone can point me in the right direction.
after quite some variations and tests it somehow started to work, using these to lines of code:
var pie = d3.layout.pie().sort(null).startAngle(-90*grad).endAngle(90*grad);
var i = d3.interpolate({startAngle: -90*grad, endAngle: -90*grad},a);
one final "problem" was that the height of the svg was too small and so some segments got cut off, so changing it to
var height = 800;
ended my search. thanks for any considerations.
A small typo on the
var svg = d3.select("body").append("svg").attr("width", width).attr("height", `height)
should be:
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height)