Related
I've copied some code to make a basic scatterplot and I'm trying to color the dots based on one of the columns of the data.
I've tried modifying the dataset to have a column named "Color" with values between 0 and 1 but when I assign the color function (i.e. d3.interpolateRdGy(d[2]) ) the scatterplot has no dots on it.
I'm very, very new to d3 (I have experience only with ggplot2 in R).
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<script>
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 60},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/2_TwoNum.csv", function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 4000])
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 500000])
.range([ height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.GrLivArea); } )
.attr("cy", function (d) { return y(d.SalePrice); } )
.attr("r", 1.5)
.style("fill", d3.interpolateRdGy(d[1]))
})
</script>
Typically with D3, you would create a color scale. If you want to map a quantitative value to a color, then you could do
const color = d3.scaleSequential()
.domain(d3.extent(data, d => d.Color))
.interpolator(d3.interpolateBlues);
d3.extent(data, d => d.Color) returns an array that contains the min and max values of the "Color" column in your dataset. This approach will work even when the values are not between 0 and 1, unlike directly calling the interpolator.
You can find other color schemes in the d3-scale-chromatic docs. For diverging color schemes, you can use a diverging scale.
Then to use the color scale, you would do
.attr('fill', d => color(d.Color))
I am looking a way how to add multiline text in one box tooltips using d3.v3.min.js . My illustration behavior like this :
And the code that I made is like this :
HTML, CSS, Javascript
function responsivefy(svg) {
// get container + svg aspect ratio
var container = d3.select(svg.node().parentNode),
width = parseInt(svg.style("width")),
height = parseInt(svg.style("height")),
aspect = width / height;
// add viewBox and preserveAspectRatio properties,
// and call resize so that svg resizes on inital page load
svg.attr("viewBox", "0 0 " + width + " " + height)
.attr("perserveAspectRatio", "xMinYMid")
.call(resize);
d3.select(window).on("resize." + container.attr("id"), resize);
// get width of container and resize svg to fit it
function resize() {
var targetWidth = parseInt(container.style("width"));
svg.attr("height", Math.round(targetWidth / aspect));
svg.attr("width", targetWidth);
}
}
var arrData = [
{"category":"Diversity & Inlusion 1", "actual":4.2, "target":5, "prediction":40, "skala":"20%"},
{"category":"Image 1", "actual":4.5, "target":4.2,"prediction":60, "skala":"40%"},
{"category":"Image 2", "actual":4.1, "target":4,"prediction":80, "skala":"60%"},
{"category":"Job Security 1", "actual":4.4, "target":4.3,"prediction":60, "skala":"100%"},
{"category":"Job Security 2", "actual":4.4, "target":4.3,"prediction":40, "skala":"100%"},
{"category":"Job Security 3", "actual":4.4, "target":4.3,"prediction":20, "skala":"100%"},
{"category":"Image 3", "actual":4.4, "target":4.3,"prediction":10, "skala":"100%"},
{"category":"Diversity & Inlusion 2", "actual":4.4, "target":4.3,"prediction":30, "skala":"100%"},
{"category":"Values", "actual":4.4, "target":4.3,"prediction":75, "skala":"100%"},
{"category":"Collaboration", "actual":4.4, "target":4.3,"prediction":45, "skala":"100%"}
];
//console.log(arrData);
//set up svg using margin conventions - we'll need plenty of room on the left for labels
var margin = {
top: 15, right: 95, bottom: 15, left: 60
};
var marginBar2 = {
top:10
};
var width = 650 - margin.left - margin.right;
var height = 768 - margin.top - margin.bottom;
var svgColor = "white";
var barHeight = 200;
//Create Main SVG
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("background-color", svgColor);
// .call(responsivefy);
//Create xScale and yScale
var xScale1 = d3.scale.linear()
.range([0, width * 0.70])
.domain([0, d3.max(arrData, function (d) {
return d.prediction;
})]);
var xScale2 = d3.scale.linear()
.range([0, width * 0.70])
.domain([0, d3.max(arrData, function (d) {
return d.target;
})]);
var xScale3 = d3.scale.ordinal()
.rangeRoundBands([0, width * 0.93])
.domain(arrData.map(function (d) {
return d.skala;
}));
var yScale = d3.scale.ordinal()
.rangeRoundBands([0, height])
.domain(arrData.map(function (d) {
return d.category;
}));
//Make yAxis to Show Category
var yAxis = d3.svg.axis()
.scale(yScale)
.tickSize(0)
.orient("left");
var gyAxis = svg.append("g")
//.attr("class", "y axis")
.attr("transform", "translate(" + width * 0.35 + "," + height * 0 + ")")
.style("text-anchor", "start")
.style("font-size", 12)
.style("font-weight", "bold")
.style("fill", "#212121")
.call(yAxis);
//Make xAxis to Show Category
// var xAxis = d3.svg.axis()
// .scale(xScale3)
// .tickSize(5)
// .orient("bottom");
// var gxAxis = svg.append("g")
// .attr("class", "x axis")
// .style("font-size", 14)
// .style("font-weight", "bold")
// .style("fill", "#212121")
// .attr("transform", "translate(" + width * 0.385 + "," + height * 1 + ")")
// .call(xAxis);
var bars1 = svg.selectAll(".bar")
.data(arrData).enter()
.append("g")
.attr("transform", "translate(" + width * 0.50 + "," + 0 + ")");
bars1.append("rect")
.attr("class", "bar")
.attr("y", function (d) {
return yScale(d.category);
})
.attr("height", barHeight - 140)
.attr("x", 0)
// .style("fill", "#c0c0c0")
.attr("width", function(d){
return xScale2(d.target);
})
.on("mouseover", function(){
tooltip1.style("display", null);
})
.on("mouseout", function(){
tooltip1.style("display", "none");
})
.on("mousemove", function(d){
var xPos = d3.mouse(this)[0] - 10;
var yPos = d3.mouse(this)[1] - 15;
tooltip1.attr("transform", "translate(" + xPos + "," + yPos +")");
tooltip1.selectAll("text").text("DI4");
tooltip1.selectAll("rect")
.attr("width", 250)
.attr("width", (function(d) {
return this.parentNode.getBBox().width;
}));
});
var bars2 = svg.selectAll(".bar2nd")
.data(arrData).enter()
.append("g")
.attr("transform", "translate(" + width * 0.50 + "," + marginBar2.top + ")");
bars2.append("rect")
.attr("class", "bar2nd")
.attr("y", function (d) {
return yScale(d.category);
})
.attr("height", barHeight - 160)
.attr("x", 0)
.attr("width", function (d) {
return xScale1(d.prediction);
})
.on("mouseover", function(){
tooltip2.style("display", null);
})
.on("mouseout", function(){
tooltip2.style("display", "none");
})
.on("mousemove", function(d){
var xPos = d3.mouse(this)[0] - 10;
var yPos = d3.mouse(this)[1] - 15;
tooltip2.attr("transform", "translate(" + xPos + "," + yPos +")");
tooltip2.selectAll("text").html("DI3");
tooltip2.selectAll("rect")
.attr("width", 250)
.attr("width", (function(d) {
return this.parentNode.getBBox().width;
}));
});
var barValue1 = svg.selectAll(".barValue1")
.data(arrData).enter()
.append("g")
.attr("transform", "translate(" + width * 0.40 + "," + height * 0.055 + ")");
barValue1.append("text")
.attr("class", "barValue1")
.attr("y", function (d) {
return yScale(d.category);
})
.attr("x", 0)
.style("font-size", 18)
.style("font-weight", "bold")
.style("fill", "#212121")
.html(function(d){
return d3.format(",.2r")(d.actual);
});
var barValue2 = svg.selectAll(".barValue2")
.data(arrData).enter()
.append("g")
.attr("transform", "translate(" + width * 0.51 + "," + height * 0.055 + ")");
barValue2.append("text")
.attr("class", "barValue2")
.attr("y", function (d) {
return yScale(d.category);
})
.attr("x", 0)
.style("font-size", 18)
// .style("font-weight", "bold")
.style("fill", "white")
.text(function(d){
return d3.format(".2%")(d.prediction / 100);
});
var tooltip1 = svg.selectAll("g.tooltip1")
.data(arrData)
.enter()
.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip1.append("rect")
// .attr("width", width * 0.7)
.attr("height", height * 0.2)
.style("fill", "white")
.style("stroke", "#969696")
.attr("stroke-width",1)
.style("opacity", 1);
tooltip1.append("text")
.attr("id", "txt1")
.attr("x", width * 0.05)
.attr("dy", height * 0.05)
.attr("font-size", 18)
.attr("font-weight", "bold");
var tooltip2 = svg.selectAll("g.tooltip1")
.data(arrData)
.enter()
.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip2.append("rect")
// .attr("width", width * 0.7)
.attr("height", height * 0.2)
.style("fill", "white")
.style("stroke", "#969696")
.attr("stroke-width",1)
.style("opacity", 1);
tooltip2.append("text")
.attr("id", "txt1")
.attr("x", width * 0.05)
.attr("dy", height * 0.05)
.attr("font-size", 18)
.attr("font-weight", "bold");
body{
font-family: "Arial", sans-serif;
}
.bar{
fill: #c0c0c0;
}
.bar:hover{
fill:rgb(95, 109, 148);
}
.bar2nd{
fill:#00315b;
}
.bar2nd:hover{
fill:#3e73b8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
is there anyone could help about my problem ? cause I need to get the code for multiline text inside tooltips box.
It might be more straightforward to append the tooltips using divs rather than svg rects - for instance, if you added:
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
Into your code, then you could make it appear and disappear plus add multi-line text using:
.on("mouseover", function(d){
var xPos = d3.mouse(this)[0];
var yPos = d3.mouse(this)[1];
tooltip.style("left", xPos + "px")
.style("top", yPos + "px")
.html("<p class='tooltip'>" + d.category + "<br><br>Line 2<br><br>Etc</p>")
.transition().delay(200).style("opacity", 0.9);
})
.on("mouseout", function(){
tooltip.transition().delay(0).style("opacity", 0);
})
You can add the same styling you had before using CSS properties, as below:
div.tooltip {
width: 200px;
height: 125px;
background-color: white;
border: 1px solid #969696;
position: absolute;
}
p.tooltip {
font-size: 18;
font-weight: 1em;
padding-left: 10px;
}
I've mocked this up in a JSFiddle here - it might have some of your code missing, apologies if so - but it should give a good idea how to merge it in if you want to use the code. Let me know if you have any questions! You should be able to get some more information from here if you need it too.
I am trying to make a d3 visual through block builder.
I want the picture of tourdefrance to appear first in the whole container and then fade away and then the bars to appear.
I know I have made it a little complicated by copying from various sources.
Would be great if someone could help out and also explain the problem.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: gold;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Mike Bostock "margin conventions"
var margin = {top: 10, right: 20, bottom: -500, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// D3 scales = just math
// x is a function that transforms from "domain" (data) into "range" (usual pixels)
// domain gets set after the data loads
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
// D3 Axis - renders a d3 scale in SVG
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
// create an SVG element (appended to body)
// set size
// add a "g" element (think "group")
// annoying d3 gotcha - the 'svg' variable here is a 'g' element
// the final line sets the transform on <g>, not on <svg>
body = d3.select("body");
show_image("https://upload.wikimedia.org/wikipedia/en/e/eb/Tour_de_France_logo.svg")
function show_image(source) {
var img = body.append("img").attr("src", source).style("opacity", 0)
; img.transition().duration(5000).ease(d3.easeLinear).style("opacity", 1)
}
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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
// d3.tsv is a wrapper around XMLHTTPRequest, returns array of arrays (?) for a TSV file
// type function transforms strings to numbers, dates, etc.
d3.csv("https://gist.githubusercontent.com/vaibhavjaitly/1418c9beb1c71f6acffdaf21669e1956/raw/45af8ebcc87d6641eee8b4a60b7bafb8326607a0/data.csv", type, function(error, data) {
replay(data);
});
function type(d) {
// + coerces to a Number from a String (or anything)
d.Frequency = +d.Frequency;
return d;
}
function replay(data) {
var slices = [];
for (var i = 0; i < data.length; i++) {
slices.push(data.slice(0, i+1));
}
slices.forEach(function(slice, index){
setTimeout(function(){
draw(slice);
}, index * 300);
});
}
function draw(data) {
// measure the domain (for x, unique letters) (for y [0,maxFrequency])
// now the scales are finished and usable
x.domain(data.map(function(d) { return d.Letter; }));
y.domain([0, d3.max(data, function(d) { return d.Frequency; })]);
// another g element, this time to move the origin to the bottom of the svg element
// someSelection.call(thing) is roughly equivalent to thing(someSelection[i])
// for everything in the selection\
// the end result is g populated with text and lines!
svg.select(".x.axis").transition().duration(300).call(xAxis);
// same for yAxis but with more transform and a title
svg.select(".y.axis").transition().duration(300).call(yAxis)
// THIS IS THE ACTUAL WORK!
var bars = svg.selectAll(".bar").data(data, function(d) { return d.Letter; }) // (data) is an array/iterable thing, second argument is an ID generator function
bars.exit()
.transition()
.duration(300)
.attr("y", y(0))
.attr("height", height - y(0))
.style('fill-opacity', 0.00001024)
.remove();
// data that needs DOM = enter() (a set/selection, not an event!)
bars.enter().append("rect")
.attr("class", "bar")
.attr("y", y(0))
.attr("height", height - y(0));
// the "UPDATE" set:
bars.transition().duration(300).attr("x", function(d) { return x(d.Letter); }) // (d) is one item from the data array, x is the scale object from above
.attr("width", x.rangeBand()) // constant, so no callback function(d) here
.attr("y", function(d) { return y(d.Frequency); })
.attr("height", function(d) { return height - y(d.Frequency); }); // flip the height, because y's domain is bottom up, but SVG renders top down
}
I'm attempting to map the beginning of bars to certain points on a y ordinal axis in d3, and it does not seem to recognize the names provided in the domain. Ultimately I'd like the chart to look like this:
This is what I've got so far (I've only provided a bit of the json because it would be huge otherwise):
var margin = {top: 50, right: 150, bottom: 50, left: 150},
w = 3000 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
d3.json("test_chart.json", function(json) {
var data = json.items;
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.starting_line + d.duration; })])
.range([0, w]);
var y = d3.scale.ordinal()
.domain(["Rome","Magdalene Castle","Herod's Palace","Pilate's Palace","King of the World's Stage","King of Flesh's stage","Stage Above Hell","Tavern","Arbor","Simon the Leper's House","Lazarus' tomb","Palace of the King of Marseilles","Sepulchre","Heathen Temple","Heaven","The Ship","The mountain","Wilderness","The priest's cell","Jherusalem","Marseilles","Hellmouth","The Place","The Lodge","The Stations","The Cloud"])
.rangeBands([0, h]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var svg = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bars = svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", function(d, i) {return "bar " + d.label;})
.attr("x", function(d, i) {return d.starting_line;})
.attr("y", function(d, i) {return d.location;})
.attr("width", function(d, i) {return d.duration})
.attr("height", 10)
.style("fill", function(d,i) {return d3.rgb(d.color)});
svg.append("g")
.attr("class", "y axis")
.attr("class", "x axis")
.call(xAxis)
.call(yAxis)
.call(bars);
// bars
var bars = svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", function(d, i) {return "bar " + d.label;})
.attr("x", function(d, i) {return d.starting_line;})
.attr("y", function(d, i) {return d.location;})
.attr("width", function(d, i) {return d.duration})
.attr("height", 10)
.style("fill", function(d,i) {return d3.rgb(d.Color)});
});
And my sample json is here:
{"items":[{"character":"Inperator","color":"CC6600","location":"Rome","starting_line":"1","duration":"19"},{"character":"Serybyl","color":"660066","location":"Rome","starting_line":"20","duration":"1"},{"character":"Inperator","color":"3300FF","location":"Rome","starting_line":"21","duration":"9"},{"character":"Provost","color":"660066","location":"Rome","starting_line":"30","duration":"1"},{"character":"Inperator","color":"CC6600","location":"Rome","starting_line":"31","duration":"11"}]}
The problem I've run into is that my .attr("y", function(d, i) {return d.location;}) statement generates the following error: Error: Invalid value for <rect> attribute y="Rome".
I'm not sure how I need to format the statement to have it map properly to my ordinal scale. Also, my bars don't appear to actually be mapping to the hex codes I provide, but I'm more concerned about the ordinal axis at the moment. I tried to handle my x axis starting point based on the advice here but I had a hard time with it and figured if I embed the starting location in the actual json I could follow the more standard model. Any advice you might be able to give me would be greatly appreciated.
You need to call your scale and pass it the location so it can do the conversion:
.attr("y", function(d, i) {
return y(d.location); //<-- your y-scale is a function, that takes the ordinal "name" and returns a pixel value
}
I am trying to visualize map and charts using leaflet.js and d3.js. I want to make the view device compatible. But my charts and maps are not device compatible. The code of showing a simple bar chart is below:
function updateCharts(data){
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 400 - margin.left - margin.right,
height = 250 - margin.top;
var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .05);
var y = d3.scale.linear().range([ height, 0 ]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(20);
x.domain(data.map(function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.speed1; })]);
var svg=d3.select("#bar").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 + ")");
var transition = svg.transition().duration(750), delay = function(d, i) {
return i * 50;
};
svg.append("text").attr("x", width / 2).attr("y", 0).style("text-anchor",
"middle").text("Speed of Lane1 Vs Time");
//Create X axis label
svg.append("text")
.attr("x", width / 2 )
.attr("y", height + margin.bottom)
.style("text-anchor", "middle")
.text("Time");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0-margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Speed");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.attr("x",5)
.style("text-anchor", "middle");
svg.selectAll("rect")
.data(data)
.enter().append("rect").transition().delay(0)
.style("fill", "red")
.attr("x", function(d,i) { return x(d.time); }) //v
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.speed1); })
.attr("height", function(d) { return height - y(d.speed1); });
//function(d){return " "+d.datetime;}
//transition.select(".y.axis").call(yAxis);
// New SVG
}
In the html I also added meta tag for device compatibility like below:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Even if the deveice size is small there appears horizontal Scrollbar. But i don't want to see such scrollbar horizontally. I want the charts and maps to be fitted within the device width. Can anyone kindly help me to solve this?
Example (note: uses jQuery):
var $graphic = $('#graphic');
function drawGraphic() {
var margin = { top: 10, right: 10, bottom: 30, left: 30 };
var width = $graphic.width() - margin.left - margin.right;
$graphic.empty();
// ... code to create the chart ...
}
d3.csv("data.csv", function(data) { //loading data, may differ
// ... manipulate data here ...
drawGraphic();
window.onresize = drawGraphic;
}
A method based on D3 responsive charts described here: http://blog.apps.npr.org/2014/05/19/responsive-charts.html
My example is described here: http://bl.ocks.org/michalskop/2fa7d4c0ae029c36ba4e
See it in action here: http://bl.ocks.org/michalskop/raw/2fa7d4c0ae029c36ba4e/
My demo for Leaflet based responsive map: http://bl.ocks.org/michalskop/raw/001f6182db52d08f4925/