Scaling Error/Axis Error in D3.js - svg

I am new to D3 and have been working along with examples and changing correcting my code using those examples,
Below is my D3 Code and it works well except the bar chart is not scaled, I seem to not understand the error on my part.
The data is coming from a TSV file and has 2 columns, 1- Categorical 2- Numerical.
<script type = "text/javascript">
var margin = { top:80, bottom:80, right:80, left:80},
width = 960 - margin.left-margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width",width + margin.left + margin.right)
.attr("height",height + margin.top + margin. bottom)
.append("g")
.attr("transform", "translate("+margin.left+"," + margin.bottom+")");
d3.tsv("ticket.tsv",function(error, data) {
dataset = data.map(function(d) { return [d["ticket"],+d["num"] ] ;})
var xScale = d3.scale.ordinal()
.domain(data.map(function(d){ return [d.ticket];}))
.rangeRoundBands([0,width],0.1);
var yScale = d3.scale.linear()
.domain([0,d3.max(data,function(d) { return Math.max([d.num]);})])
.range([height, 0]);
var rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d,i) { return xScale(d.ticket);})
.attr("y",function (d) { return height - (yScale (d.num)) ;})
.attr("height",function (d) { return d.num;})
.attr("width",xScale.rangeBand())
.attr("fill","orange");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0"+","+(height)+")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.call(yAxis);
;});
</script>

Here is the Solved Code and Picture, I have commented the code I have corrected.
<script type = "text/javascript">
var margin = { top:80, bottom:80, right:80, left:80},
width = 960 - margin.left-margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width",width + margin.left + margin.right)
.attr("height",height + margin.top + margin. bottom)
.append("g")
.attr("transform", "translate("+margin.left+"," + margin.bottom+")");
d3.tsv("tickets.tsv",function(error, data) {
dataset = data.map(function(d) { return [d["ticket"],+d["num"] ] ;})
var xScale = d3.scale.ordinal()
.domain(data.map(function(d){ return [d.ticket];}))
.rangeRoundBands([0,width],0.1);
var yScale = d3.scale.linear()
.domain([0,d3.max(data,function(d) { return Math.max([d.num]);})])
.range([height,0]);
var rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d) { return xScale(d.ticket);})
.attr("y",function (d) { return yScale (d.num) ;})**//I made the change HERE**
.attr("height",function (d) { return height - yScale(d.num);})**// and HERE**
.attr("width",xScale.rangeBand())
.attr("fill","orange");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0"+","+(height)+")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.call(yAxis);
;});
</script>

Related

Add constant horizontal line

I drew a multi-line chart that changes based on some user input. I want to add a reference line to always appear at the value y=100. I was able to manually place a line, but it is not always exactly at y=100.
Formatting issues aside, this is what I have for one possible user input. As you can see, the reference line is slightly below 100:
And my code:
const svg = d3.select("svg");
const width = +svg2.attr("width");
const height = +svg2.attr("height");
const render = data =>{
const xValue = d => +d.week;
const yValue = d => +d.power_score;
const margin = {top:50, right:70, bottom:60, left:20};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const colorValue = d => d.team;
// define scales
const xScale = d3.scaleLinear()
.domain([1, d3.max(data, xValue)])
.range([0, innerWidth-250])
.nice();
const yScale = d3.scaleLinear()
.domain([d3.min(data, yValue)-10, d3.max(data, yValue)+10])
.range([innerHeight, 0])
.nice();
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
const g = svg2.append("g")
.attr('transform', 'translate(75, 50)');
// create axes
const xAxis = d3.axisBottom(xScale)
.tickSize(-innerHeight-10);
const yAxis = d3.axisLeft(yScale)
.tickSize(-innerWidth+240);
const xAxisG = g.append("g").call(xAxis)
.attr("transform", "translate(0, 400)");
xAxisG.select(".domain")
.remove();
xAxisG.append("text")
.attr("class", "axis-label")
.attr("y", 40)
.attr("x", (innerWidth-250)/2)
.attr("fill", "black")
.text("Week");
const yAxisG = g.append("g").call(yAxis)
.attr("transform", "translate(-10, 0)")
.select(".domain")
.remove();
yAxisG.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", -35)
.attr("x", -innerHeight/4)
.attr("fill", "black")
.text("Power Score");
// generate line
const lineGenerator = d3.line()
.x(d => xScale(xValue(d)))
.y(d => yScale(yValue(d)));
// sort data for legend
const lastYValue = d =>
yValue(d.values[d.values.length - 1]);
// group data
const nested = d3.nest()
.key(colorValue)
.entries(data)
.sort((a, b) =>
d3.descending(lastYValue(a), lastYValue(b)));
colorScale.domain(nested.map(d => d.key));
// manually add horizonal line here
svg2.append("g")
.attr("transform", "translate(75, 267)")
.append("line")
.attr("x2", innerWidth-250)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-dasharray", "3, 3");
// add lines with mouseover effect
g.selectAll(".line-path").data(nested)
.enter().append("path")
.attr("class", "line-path")
.attr("d", d => lineGenerator(d.values))
.attr("stroke", d => colorScale(d.key))
.attr("stroke-width", "3")
.attr("opacity", "0.5")
.on("mouseover", function(d, i) {
d3.select(this).transition()
.duration("50")
.attr("stroke-width", "5")
.attr("opacity", "1")})
.on("mouseout", function(d, i) {
d3.select(this).transition()
.duration("50")
.attr("stroke-width", "3")
.attr("opacity", "0.5")});
d3.line()
.x(d => xScale(xValue(d)))
.y(d => yScale(yValue(d)));
// draw legend
const colorLegend = (selection, props) => {
const {
colorScale,
circleRadius,
spacing,
textOffset
} = props;
const groups = selection.selectAll('g')
.data(colorScale.domain());
const groupsEnter = groups
.enter().append('g')
.attr('class', 'tick');
groupsEnter
.merge(groups)
.attr('transform', (d, i) =>
`translate(0, ${i * spacing})`
);
groups.exit().remove();
groupsEnter.append('circle')
.merge(groups.select('circle'))
.attr('r', circleRadius)
.attr('fill', colorScale);
groupsEnter.append('text')
.merge(groups.select('text'))
.text(d => d)
.attr('dy', '0.32em')
.attr('x', textOffset);
}
svg2.append("g")
.attr("transform", "translate(710, 60)")
.call(colorLegend, {
colorScale,
circleRadius: 4,
spacing: 15,
textOffset: 8
});
// Title
g.append("text")
.attr("class", "title")
.attr("x", (innerWidth-250)/4)
.attr("y", -10)
.text("Weekly Power Ranks");
};
d3.csv('data.csv').then(data => {
data.forEach(d => {
d.week = +d.week;
d.power_score = +d.power_score;
});
render(data);
});
Instead of using magic numbers...
svg2.append("g")
.attr("transform", "translate(75, 267)")
//magic numbers---------------^----^
...use the same y scale you're using to paint the paths:
g.append("g")
.attr("transform", `translate(75, ${yScale(100)})`)
Also, append it to the already translated <g>, or apply the same translation (again, more magic numbers in your code...).

Problems loading CSV data in D3. svg.selectAll(...).data(...).enter is not a function

I am trying to load a CSV data set into d3 by assigning it to a variable, but it seems that I keep receiving an error saying that enter() is not a function. I think the issue lies in the way I'm loading the CSV data.
For reference, I'm following this tutorial: http://duspviz.mit.edu/d3-workshop/scatterplots-and-more/
Here is my code for reference.
var ratData = [];
d3.csv("rat-data.csv", function(d) {
return {
city : d.city, // city name
rats : +d.rats // force value of rats to be number (+)
};
}, function(error, rows) { // catch error if error, read rows
ratData = rows; // set ratData equal to rows
console.log(ratData);
createVisualization(); // call function to create chart
});
function createVisualization(){
// Width and height of SVG
var w = 150;
var h = 175;
// Get length of dataset
var arrayLength = ratData.length; // length of dataset
var maxValue = d3.max(ratData, function(d) { return +d.rats;} ); // get maximum
var x_axisLength = 100; // length of x-axis in our layout
var y_axisLength = 100; // length of y-axis in our layout
// Use a scale for the height of the visualization
var yScale = d3.scaleLinear()
.domain([0, maxValue])
.range([0, y_axisLength]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Select and generate rectangle elements
svg.selectAll( "rect" )
.data( ratData )
.enter()
.append("rect")
.attr( "x", function(d,i){
return i * (x_axisLength/arrayLength) + 30; // Set x coordinate of rectangle to index of data value (i) *25
})
.attr( "y", function(d){
return h - yScale(d.rats); // Set y coordinate of rect using the y scale
})
.attr( "width", (x_axisLength/arrayLength) - 1)
.attr( "height", function(d){
return yScale(d.rats); // Set height of using the scale
})
.attr( "fill", "steelblue");
// Create y-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 75)
.attr("x2", 30)
.attr("y2", 175)
.attr("stroke-width", 2)
.attr("stroke", "black");
// Create x-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 175)
.attr("x2", 130)
.attr("y2", 175)
.attr("stroke-width", 2)
.attr("stroke", "black");
// y-axis label
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.text("No. of Rats")
.attr("transform", "translate(20, 20) rotate(-90)")
.attr("font-size", "14")
.attr("font-family", "'Open Sans', sans-serif");
}; // end of function

Mark a location on an svg map in d3.js

I am using this example from the d3.js wiki.
http://bl.ocks.org/2206590
From that, I have a map, and I want to know how to mark a single location on it.
How do I plot a small circle on this map at the location with co-ordinates [40.717079,-74.009628].
Here is the source code from the example:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", click);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.append("g")
.attr("id", "states");
var projection = d3.geo.albersUsa()
.scale(width)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
d3.json("readme.json", function(json) {
g.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path);
});
OK this may not be the right answer, but this is what I did:
var width = 1060,
height = 600,
centered;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", click);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.append("g")
.attr("id", "states");
var projection = d3.geo.albersUsa()
.scale(width)
.translate([0, 100]);
var path = d3.geo.path()
.projection(projection);
setInterval(function(){
draw();
},1000);
function draw(){
d3.json("readme.json", function(json) {
g.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.on("click", click);
var latitude = 35 + Math.floor(Math.random()*8);
var longitude = -1 * (83 + Math.floor(Math.random()*35));
var coordinates = projection([longitude, latitude]);
g.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 2)
.attr('stroke','red')
.attr('fill','red');
});
}
The code works, but I suspect I am doing the wrong thing by redrawing the whole map every time I render a new co-ordinate.

d3 Gives a 406 Error when trying to run d3js example on IIS

I have setup a basic IIS server and am trying to demonstrate a d3js example. First I create an html page with the example code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
and then i create the data.csv file:
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
AL,310504,552339,259034,450818,1231572,1215966,641667
AK,52083,85640,42153,74257,198724,183159,50277
AZ,515910,828669,362642,601943,1804762,1523681,862573
AR,202070,343207,157204,264160,754420,727124,407205
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
CO,358280,587154,261701,466194,1464939,1290094,511094
CT,211637,403658,196918,325110,916955,968967,478007
DE,59319,99496,47414,84464,230183,230528,121688
DC,36352,50439,25225,75569,193557,140043,70648
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
GA,740521,1250460,557860,919876,2846985,2389018,981024
HI,87207,134025,64011,124834,356237,331817,190067
ID,121746,201192,89702,147606,406247,375173,182150
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
IN,443089,780199,361393,605863,1724528,1647881,813839
IA,201321,345409,165883,306398,750505,788485,444554
KS,202529,342134,155822,293114,728166,713663,366706
KY,284601,493536,229927,381394,1179637,1134283,565867
LA,310716,542341,254916,471275,1162463,1128771,540314
ME,71459,133656,69752,112682,331809,397911,199187
MD,371787,651923,316873,543470,1556225,1513754,679565
MA,383568,701752,341713,665879,1782449,1751508,871098
MI,625526,1179503,585169,974480,2628322,2706100,1304322
MN,358471,606802,289371,507289,1416063,1391878,650519
MS,220813,371502,174405,305964,764203,730133,371598
MO,399450,690476,331543,560463,1569626,1554812,805235
MT,61114,106088,53156,95232,236297,278241,137312
NE,132092,215265,99638,186657,457177,451756,240847
NV,199175,325650,142976,212379,769913,653357,296717
NH,75297,144235,73826,119114,345109,388250,169978
NJ,557421,1011656,478505,769321,2379649,2335168,1150941
NM,148323,241326,112801,203097,517154,501604,260051
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
NC,652823,1097890,492964,883397,2575603,2380685,1139052
ND,41896,67358,33794,82629,154913,166615,94276
OH,743750,1340492,646135,1081734,3019147,3083815,1570837
OK,266547,438926,200562,369916,957085,918688,490637
OR,243483,424167,199925,338162,1044056,1036269,503998
PA,737462,1345341,679201,1203944,3157759,3414001,1910571
RI,60934,111408,56198,114502,277779,282321,147646
SC,303024,517803,245400,438147,1193112,1186019,596295
SD,58566,94438,45305,82869,196738,210178,116100
TN,416334,725948,336312,550612,1719433,1646623,819626
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
UT,268916,413034,167685,329585,772024,538978,246202
VT,32635,62538,33757,61679,155419,188593,86649
VA,522672,887525,413004,768475,2203286,2033550,940577
WA,433119,750274,357782,610378,1850983,1762811,783877
WV,105435,189649,91074,157989,470749,514505,285067
WI,362277,640286,311849,553914,1487457,1522038,750146
WY,38253,60890,29314,53980,137338,147279,65614
When I access the page, nothing displays...A quick look through fiddler shows the html content downloads fine (you can also see it in show source). You can also look at the data.csv file directly by accessing it from the url
http://localhost/data.csv
The problem shown in fiddler is a 406 error when d3js attempts to load the CSV file. Any ideas?
Thanks
What does your HTTP Request's Accept header contain? Apache is probably configured in such a way so as to return a 406 because the value in the Accept header does not include whatever MIME type your CSV is returning.
See
http://blogs.msdn.com/b/ieinternals/archive/2011/03/27/http-406-not-acceptable-php-ie9-standards-mode-accepts-only-text_2f00_css-for-stylesheets.aspx for a similar problem sometimes seen in browsers.
Change the build action for the csv file to "Resource" in the file's properties using your Visual Studio.

Svg clip-path within rectangle does not work

I have a simple graph with x and y axes. I don't want the drawing area I draw within to overlap the axes.
I'm using d3 to create my chart but the clip-path does not work:
http://jsfiddle.net/EqLBJ/
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scale.linear().
domain([xMin, xMax]). // your data minimum and maximum
range([0, width]); // the pixels to map to, e.g., the width of the diagram.
var yScale = d3.scale.linear().
domain([yMax, yMin]).
range([0, height]);
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(10, d3.format(",d")),
yAxis = d3.svg.axis().orient("left").scale(yScale);
var chart = d3.select("#chart").append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("pointer-events", "all")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.behavior.zoom().scaleExtent([0.2, 5]).on("zoom", redraw));
var rect = chart.append('svg:rect')
.attr('width', width)
.attr('height', height)
.attr('fill', 'white');
var line = d3.svg.line()
.interpolate("basis")
.x(function(d, i) { return xScale(d.time); })
.y(function(d) { return yScale(d.value); });
var clip = chart.append("svg:clipPath")
.attr("id", "clip");
clip.append("svg:rect")
.attr("id", "clip-rect")
.attr("width", width)
.attr("height", height);
// .attr("fill", "white");
var path = chart.append("svg:path")
.attr("clip-path", "url(#clip-rect)")
.data([data])
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "maroon")
.attr("stroke-width", 2)
.attr("d", line);
// x-axis label
chart.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height - 6)
.text("time");
// y-axis label
chart.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("value");
// x-axis
var xaxis = chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// y-axis
var yaxis = chart.append("g")
.attr("class", "y axis")
.call(yAxis);
function redraw()
{
console.log("here", d3.event.translate, d3.event.scale);
path.transition()
.ease("linear")
.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
You want something like this:
http://jsfiddle.net/dsummersl/EqLBJ/1/
Specifically:
use 'clip' instead of 'clip-rect'
put the content you wish to clip inside a 'g' element, and specify the 'clip-path' attribute and the transforms for the 'g' element.

Resources