D3 Programmatic defs pattern fill for svg shapes - d3.js

I wanted to create a circular crop an image to use in conjunction with a svg circle, and decided on filling the svg circle with a pattern. I have it working for the single case:
defs.append("svg:pattern")
.attr("id", "story1")
.attr("width", 200)
.attr("height", 100)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", 'ml-image4.png')
.attr("width", 200)
.attr("height", 100)
.attr('x',0)
.attr('y',0);
var circle1 = svg.append("circle")
.attr("cx", 100)
.attr("cy", 50)
.attr("r", 40)
.style("fill", "#fff")
.style("fill", "url(#story1)")
.style('stroke','#000')
.style('stroke-width', 5);
Then I got all gung-ho and tried to adapt for a programmatic implementation, but it didn't pan out so well. I'm hoping there is a way to do it, because my end goal is to have an application that uses many pictures as patterns. It will be like a little gallery where each svg circle is spaced out across the page using .attr('x', function(d,i) { return 100+i*200})) and each circle referencing a different pattern. The pattern id is a data member, so I was trying to do this: .style('fill',function(d) {return "url(#"+d.id+")"; }). I'm not exactly sure why it didn't work; to my eyes it seems functional-ish. I did get the error:
Uncaught DOMException: Failed to execute 'querySelectorAll' on
'Element': 'svg:pattern' is not a valid selector.
Here is my adaptation for programmatic def patterns and a quick look at my data:
var data = [
{'id':'story1', 'Title':'Title1', 'Date':'03/10/2017','Icon':'Icon1.png', 'Link':'www.link1.com/'},
{'id':'story2', 'Title':'Title2', 'Date':'12/15/2017','Icon':'Icon2.png', 'Link':'www.link2.com/'}
];
var defs = svg.append('svg:defs');
defs.selectAll(null)
.data(data)
.enter()
.append('svg:pattern')
.attr('id',function(d) {return d.id; })
.attr('width', 200)
.attr('height', 100)
.attr('patternUnits', 'userSpaceOnUse')
.append('svg:image')
.attr('xlink:href', function(d) {return d.Icon})
.attr('width', 200)
.attr('height', 100)
.attr('x',0)
.attr('y',0);
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d,i) {return 100+i*200})
.attr('cy', 50)
.attr('r',40)
.style('fill',function(d) {return "url(#"+d.id+")"; })
.style('stroke','#000')
.style('stroke-width',3);
Question
Judging by the error, my adaptation seems to be flawed. What can I tweak to get programmatic patterns for svg circles here? In my humble opinion, my approach isn't all that different from the simple case at the very beginning of the post (1 pattern 1 circle), all I was trying to do was scale it up so I can use d3.selectAll() using my data.

Related

Cannot get d3 svg tutorial to work in jsfiddle

I'm trying to follow a d3 tutorial and I've created a JSFiddle for the following code
var dataset = [1,2,3,4,5];
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 75);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "red")
.attr("height", 40)
.attr("width", 75)
.attr("x", function(d, i){return i*80})
.attr("y", 20);
However, I see the generated circles in the svg but I can't see them on the screen. Can anyone see what I'm missing?
Here is a FIDDLE:
var dataset = [1,2,3,4,5];
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "red")
.attr("cx", function(d, i){return (i + 1 ) *60})
.attr("cy", 30)
.attr("r", 20);
I just focused on the main parts that needed change. You can study the differences. Basically, you had the wrong attributes for a circle (x and y, instead of cx and cy) and was missing the radius attribute. Finally, height and width are not circle attributes.

Backfill a D3 bar chart with gray to show 100% vs. actual

I'm trying to get a gray fill in the background to show 100% with the actual percentage fill in the foreground, please see the image:
Image of what I'm trying to do
JS Fiddle Example
//load the status'
var theStatus = svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", function (d) { return (d.status); })
.attr("width", 0)
.attr("height", 20);
//animate the status
theStatus.transition()
.attr("width", function (d) { return xBarScale(d.percent); })
.attr("y", function (d) { return yBarScale(d.id)-10; })
.duration(animationDur2);
});
Any help is appreciated!!
You can do this by appending another set of rectangles with grey fill and constant width:
svg.selectAll(".background")
.data(data)
.enter()
.append("rect")
.attr("class", "background")
.attr("y", function (d) { return yBarScale(d.id)-10; })
.attr("width", width)
.attr("height", 20);
Complete jsfiddle here.

generate circle for each line of CSV D3.js

I'm trying to read in a CSV and generate a circle for each line using d3.js.
[I'd prefer not to use a callback function but that's a different question.]
I'm able to create a table for each line of data following: http://christopheviau.com/d3_tutorial/
but I can't seem to generate a circle for each line:
d3.text("test.csv", function(datasetText) {
var parsedCSV = d3.csv.parseRows(datasetText);
var sampleSVG2 = d3.select("#viz")
.append("svg")
.attr("width", 100)
.attr("height", 100);
sampleSVG2.selectall("circle")
.data(parsedCSV)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "blue")
This is probably not "correct" but it works. By calling a new svg area for each row and each circle it worked.
var sampleSVG = d3.select("#potato")
.data(parsedCSV)
.enter().append("circle")
.append("svg")
.attr("width", 100)
.attr("height", 100);
sampleSVG.append("circle")
.style("stroke", "gray")
.style("fill", "blue")
.attr("r", 40)
.attr("cx", 50)
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "red");})
.on("mouseout", function(){d3.select(this).style("fill", "steelblue");});

Zooming bar-graph with d3.js

I am trying to create a bar graph with a time scale where its possible to zoom into any time period. I was able to create the zooming functionality for my x-axis(time scale) however my data (in this case rects) doesn't zoom along with my axis.
Here is a simplified version of my graph: http://jsfiddle.net/gorkem/Mf457/5/embedded/result/
As you can see I can zoom in to my x-axis but the bars do not zoom along.
and here is the jfiddle link: http://jsfiddle.net/gorkem/Mf457/6/
var y = d3.scale.linear().domain([0, max_val]).range([graph_height, 0]);
var x = d3.time.scale().domain([minDate, maxDate]).range([0, graph_width]);
var chart = d3.select(location).append("svg")
.attr("class", "chart")
.attr("width", graph_width+20)
.attr("height", graph_height+20)
.call(d3.behavior.zoom().x(x).scaleExtent([1, 8]).on("zoom", zoom));
var lines = chart.selectAll("line");
var lines_y = lines
.data(x.ticks(5))
.enter().append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", function (d) {return graph_height - 20 - d;})
.attr("y2", graph_height)
.style("stroke", "#ccc");
var lines_x = lines
.data(y.ticks(10))
.enter().append("line")
.attr("x1", 0)
.attr("x2", graph_width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
xAxis = d3.svg.axis().scale(x);
yAxis = d3.svg.axis().scale(y).orient("left");
chart.append("svg:g")
.attr("class", "xaxis")
.attr("transform","translate(0,300)")
.call(xAxis);
chart.append("svg:g")
.attr("class", "yaxis")
.attr("transform", "translate(25,0)")
.call(yAxis);
var rect = chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d,i) {return x(new Date(d["date"]))+20; })
.attr("y", function (d,i) { return graph_height - (d["num"] *v_scale);})
.attr("width", x(new Date(data[1]["date"])))
.attr("height", function (d,i) {return d["num"]*v_scale;});
rect.call(d3.behavior.zoom().x(x).scaleExtent([1, 8]).on("zoom", zoom));
function zoom() {
chart.select(".xaxis").call(xAxis);
chart.select(".yaxis").call(yAxis);
}
}
I feel like I should be adding more functionality to my zoom function for the function to be effective on bars(rects). I would really appreciate any help.
Using 'selectAll', I've applied the scaling and translation to each bar respectively, restricting both scale and translation to the x-axis only.
function zoom() {
chart.select(".xaxis").call(xAxis);
chart.select(".yaxis").call(yAxis);
chart.selectAll(".chart rect").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)");
}
This works equally well with a single path element, which is what I was trying to figure out when I stumbled upon your question.
I'm wondering the same thing, so at least know you're in good company. Here's my current approach. First of all, I can't generate a plot from your code and the links don't send me to anything useful -- no chart renders. So I can't help with your specific problem. However, there is an example that has zoomable rectangles here: http://bl.ocks.org/1962173. I'm currently going through it and deleting unnecessary elements and seeing if I can make it work. Hopefully this helps!

D3.js - can clipping paths be used with d3.svg.symbol?

I'm trying to draw multiple 'cross' symbols inside of a circle for use in a visualization. I'd like to draw the crosses in a 'g' tag and then apply a clipping path.
Is it possible to use clip paths with d3.svg.symbol ?
In the below example, the svg circle is masked correctly with the clip path; however the cross (the last part of the code) isn't.
Am I doing something wrong or is this not a feature?
var svg = d3.select("#maskingExample")
.append("svg:svg")
.attr("width", 500)
.attr("height", 200);
svg.append("svg:clipPath")
.attr("id", "clipper")
.append("svg:rect")
.style("stroke", "gray")
.style("fill", "black")
.attr("x", 50)
.attr("y", 25)
.attr("width", 300)
.attr("height", 45);
svg.append("g").append("svg:circle")
.style("stroke", "gray")
.style("fill", "blue")
.attr("cx", 175)
.attr("cy", 55)
.attr("r", 50)
.attr("clip-path", "url(#clipper)");
svg.append("g").append("path")
.attr("d", d3.svg.symbol()
.size( function(d) { return 3000; })
.type( function(d) { return d3.svg.symbolTypes[1]; }))
.attr("transform", "translate(150, 50)")
.attr("clip-path", "url(#clipper")
.style("fill", "black");
You're missing a close paren. Instead of
.attr("clip-path", "url(#clipper")
it should read
.attr("clip-path", "url(#clipper)")

Resources