How to insert images into d3 hexbin - image

I created a mesh of hexagons using D3's hexbin. I want to fill the hexagons with images. I took a look at this question where a circle is filled with an image. I tried to do exactly the same and fill every bin with the image but it didn't work.
This is what i tried:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.hexagon {
fill: none;
stroke: #000;
stroke-width: 2px;
}
</style>
<body>
<svg id="mySvg" width="80" height="80">
<defs id="mdef">
<pattern id="image" x="0" y="0" height="40" width="40">
<image x="0" y="0" width="40" height="40" xlink:href="http://www.e-pint.com/epint.jpg"></image>
</pattern>
</defs>
</svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.hexbin.v0.min.js?5c6e4f0"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.linear()
.domain([0, 20])
.range(["orange", "orange"])
.interpolate(d3.interpolateLab);
var hexbin = d3.hexbin()
.size([width, height])
.radius(100);
var points = hexbin.centers();
var x = d3.scale.identity()
.domain([0, width]);
var y = d3.scale.linear()
.domain([0, height])
.range([height, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("class", "mesh")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".hexagon")
.data(hexbin(points))
.enter().append("path")
.attr("class", "hexagon")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style("fill", 'url(#image)');
</script>
So how can i fill the hexagons with an image?

Looks like your pattern just wasn't defined in a way that would show up in the path. This should move the image to the right part of the pattern to show up in your hex grid.
<pattern id="image" x="-100" y="-100" height="200" width="200">
<image x="50" y="50" width="100" height="100" xlink:href="http://www.e-pint.com/epint.jpg"></image>
</pattern>

Related

How do implement d3 v3 brush by specifying a increment brush slide value in steps programmatically

I am trying to implement the brush behavior as in this example. This example is in d3v4.
URL: https://bl.ocks.org/alexmacy/eb284831aff6f9d0119b
We mention this below line. So the difference between the two is always 30 when you slide:
brush.move(brushg, [20, 50].map(x));
My query is: can we do the same in d3v3 and what I require is the 30 difference what we are getting initially on the page load should be set programmatically. I mean the difference value eg: 30,20 etc
Also, consider another example with dates (start and end date) when we slide - the increment has to be in steps of the configured value that is what value we specify programmatically.
Suppose: we specify step value as 5 and
startdate: 11/25/2019 10:00:00 AM, enddate: 11/25/2019 10:30:00 AM
Now when we slide the slider, the values should be in seconds as:
10:00:05 am, 10:00:10 am, 10:00:15 am, 10:00:20 am, 10:00:25 am, 10:00:30 am
step value as 2 then
10:00:02 am, 10:00:04 am, 10:00:06 am, 10:00:08 am, 10:00:10 am, 10:00:12 am ...
By default the step value is 1
Iam using d3v3, for specifying domain & range:
textScale = d3.scale.linear().domain([startDate, endDate]).range([0, main_width]).clamp(true);
var axis = d3.svg.axis().scale(this.textScale).orient('Bottom');
I am implementing in d3v3.
This is too late but in case someone came across this issue for D3 v3:
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 900 - margin.left - margin.right,
height = 20;
var x = d3.scale.linear()
.domain([0,100])
.range([0, width]);
var brush = d3.svg.brush()
.x(x)
.extent([20, 50]);
var svg = d3.select("#slider").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
.tickFormat(d3.format("")));
var brushg = svg.append("g")
.attr("class", "brush")
.call(brush);
brushg.selectAll("rect")
.attr("height", height);
brush.on('brush', function() {brushed()})
d3.select('#start-number')
.append('text')
.text(brush.extent()[0]);
d3.select('#end-number')
.append('text')
.text(brush.extent()[1]);
function brushed() {
d3.select('#start-number')
.text(Math.round(brush.extent()[0]));
d3.select('#end-number')
.text(Math.round(brush.extent()[1]));
}
brushed();
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
<div id='numbers'>
<span id='start-number'></span> to <span id='end-number'>
</span>
</div>
<div id='slider' style="margin-top:1%"></div>
<script src="//d3js.org/d3.v3.min.js"></script>
</body>
</html>
For the date slider, it's not implemented same as described above in the question but a place to start from, here:
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 900 - margin.left - margin.right,
height = 20;
var x = d3.time.scale()
.domain([new Date('11/25/2019 10:00:00 AM'), new Date('11/25/2019 10:30:00 AM')])
.nice(d3.time.second)
.range([0, width]);
var brush = d3.svg.brush()
.x(x)
.extent([new Date('11/25/2019 10:10:00 AM'), new Date('11/25/2019 10:15:00 AM')]);
var svg = d3.select("#slider").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(10)
.tickFormat(d3.time.format("%H:%M"))
);
var brushg = svg.append("g")
.attr("class", "brush")
.call(brush);
brushg.selectAll("rect")
.attr("height", height);
brush.on('brush', function() {brushed()})
var format = d3.time.format("%H:%M:%S");
d3.select('#start-number')
.append('text')
.text(format(brush.extent()[0]));
d3.select('#end-number')
.append('text')
.text(format(brush.extent()[1]));
function brushed() {
d3.select('#start-number')
.text(format(brush.extent()[0]));
d3.select('#end-number')
.text(format(brush.extent()[1]));
}
brushed();
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
<div id='numbers'>
<span id='start-number'></span> to <span id='end-number'>
</span>
</div>
<div id='slider' style="margin-top:1%"></div>
<script src="//d3js.org/d3.v3.min.js"></script>
</body>
</html>

D3 tick with background

How can a D3 axis tick have a background color?
A brute way of doing so it to append a rect element inside each g.tick and have a fill on it, but it's quite difficult to achieve, since the rect has to be the same size as the text inside the tick..
Here's a basic ticks example by Mike Bostock (and another with graph)
I took a screenshot and marked (red border) where I want the ticks to have background color:
Does anyone know of any sane way of having background color on Ticks?
Thanks
Another option would be to make a filter like this:
var filter = svg.append("defs").append("filter")
.attr("x", "0")
.attr("y", "0")
.attr("width", "1")
.attr("height", "1")
.attr("id", "background")//id of the filter
filter.append("feFlood")
.attr("flood-color", "red");
filter.append("feComposite")
.attr("in", "SourceGraphic");
and to ticks add the filter like this:
g.selectAll(".tick text").attr("filter","url(#background)");
working code here
I wouldn't dismiss your rect idea so quickly. It's pretty simple to implement and allows you to adjust the size of the "background" however you want. Here's how it would look with your 3 extra pixel:
d3.selectAll(".tick").each(function(d,i){
var tick = d3.select(this),
text = tick.select('text'),
bBox = text.node().getBBox();
tick.insert('rect', ':first-child')
.attr('x', bBox.x - 3)
.attr('y', bBox.y - 3)
.attr('height', bBox.height + 6)
.attr('width', bBox.width + 6)
.style('fill', d3.schemeCategory20[i % 20]);
});
Full example:
<!DOCTYPE html>
<meta charset="utf-8">
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 0, bottom: 20, left: 0},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scalePoint()
.domain([0, 1, 2])
.range([0, width])
.padding(1);
var y = d3.scaleLinear()
.domain([-1e6, 2e6])
.range([height, 0]);
g.append("g")
.attr("transform", "translate(" + x(0) + ",0)")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(20, "s"));
g.append("g")
.attr("transform", "translate(" + x(1) + ",0)")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(20)
.tickFormat(d3.format(".0s")));
g.append("g")
.attr("transform", "translate(" + x(2) + ",0)")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(20)
.tickFormat(d3.formatPrefix(".1", 1e6)));
d3.selectAll(".tick").each(function(d,i){
var tick = d3.select(this),
text = tick.select('text'),
bBox = text.node().getBBox();
tick.insert('rect', ':first-child')
.attr('x', bBox.x - 3)
.attr('y', bBox.y - 3)
.attr('height', bBox.height + 6)
.attr('width', bBox.width + 6)
.style('fill', d3.schemeCategory20[i % 20]);
});
</script>
SVG text background color with padding
<svg width="200" height="200">
<defs>
<filter x="-0.5" y="-0.5" width="2" height="2" id="solid">
<feFlood flood-color="#BDBDBD"></feFlood>
<feComposite in="SourceGraphic"></feComposite></filter>
</defs>
<text x="50" y="50" font-size="13" fill="#fff" filter="url(#solid)">7%</text>
</svg>

How to drag element after cloning

How to drag a cloned element after appending to parent node. I have tried with the drag method but it isn't working. I am new to d3 js and trying to go basics and examples on fiddle.
Demo: https://jsfiddle.net/pmczny6k/
code:
<html>
<head>
<title>Editor</title>
<meta http-equiv="x-ua-compatible" content="ie=9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
window.onload = function ()
{
var svgContainer = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
var circle = svgContainer.append("circle")
.attr("cx", 30)
.attr("cy", 30)
.attr("r", 20);
var rectangle = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 50)
.attr("height", 50);
//clonning
function move() {
d3.select(rectangle)
.attr('x', d3.event.x - parseInt(d3.select(this).attr("width")) / 2)
.attr('y', d3.event.y - parseInt(d3.select(this).attr("height")) / 2);
this.parentNode.appendChild(this);
}
;
d3.selectAll(".drg").style("fill", "red")
.call(
d3.behavior.drag()
.on('drag', move).origin(function () {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
})
.on('dragend', function (d) {
var elem = d3.select(this);
elem.attr("x", elem.attr("initial-x"));
elem.attr("y", elem.attr("initial-y"));
console.log(elem.attr("x"));
var mouseCoordinates = d3.mouse(this);
if (mouseCoordinates[0] > 70) {
//Append new element
d3.select("svg").append("rect")
.classed("drg", true)
.attr("width", 50)
.attr("height", 50)
.attr("x", mouseCoordinates[0])
.attr("y", mouseCoordinates[1])
.style("fill", "green");
}
})
)
};
</script>
</head>
<body>
<svg width="1024" height="768" style="background-color: #204d74">
<!--<g>-->
<rect x="10" y="20" height="250" width="300" style="fill: #080808"></rect>
<rect class="drg" x="12" y="22" initial-x="12" initial-y="22" height="50" width="50" style="fill: #f0ad4e"></rect>
<!--</g>-->
<rect x="10" y="280" height="250" width="300" style="fill: #080808"></rect>
<rect class="drg" x="12" y="282" initial-x="12" initial-y="282" height="50" width="50" style="fill: #f0ad4e"></rect>
<rect x="320" y="20" height="510" width="690" style="fill: #080808"></rect>
</svg>
</body>
</html>
Just added another call to the new created rect object:
.call(
d3.behavior.drag()
.on('drag', move).origin(function() {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
}));
Updated fiddle.
You also have some syntax errors. Please check them on a better syntax highlighting tool as fiddle isn't a good way to do this. Hope it works now as expected.

d3 rangeRoundBands iternal calculation

I'm trying to add some padding between my bars, but can't control,say understand, it accurately.
Here's the code snippet I'm following from here:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 30, 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()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand());
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
Data.dsv file content:
name value
A 12
B 13
C 22
D 5
Then, we you load it and query from developer's console, you know
> x.range()
[24, 258, 492, 726]
> x.rangeBand()
211
, but I wondered how these numbers be determined, the internal calcuation.Any help would be much appreciated!

How to apply a pattern for a D3 bar chart?

I am trying to apply a pattern to a D3 bar chart, but what I get is this:
the chart should stop exactly at 100,000
the pattern should be "fluid"
I am using a green and red pattern defined as follows:
var defs = this.svg.append("g:defs");
defs.append("g:pattern")
.attr("id", "red-fill")
.attr("patternUnits", "userSpaceOnUse")
.attr("width", "85")
.attr("height", "10")
.append("g:image")
.attr("xlink:href", "../10px-barchart-red.png")
.attr("x", 0)
.attr("y", 0)
.attr("width", 85)
.attr("height", 10);
var defs = this.svg.append("g:defs");
defs.append("g:pattern")
.attr("id", "green-fill")
.attr("patternUnits", "userSpaceOnUse")
.attr("width", "85")
.attr("height", "10")
.append("g:image")
.attr("xlink:href", "../10px-barchart-green.png")
.attr("x", 0)
.attr("y", 0)
.attr("width", 85)
.attr("height", 10);
And the plot is made with:
this.svg.selectAll("rect")
.data(dataset, getKeys)
.enter()
.append("rect")
.attr('class', 'bar')
.attr("x", function(d, i) {
return x(i) + 44;
})
.attr("y", function(d, i) {
return y(d.value);
})
.attr("width", x.rangeBand())
.attr("height", function(d, i) {
return height + padding - y(d.value);
})
.attr("fill", function(d) {
if (d.key == 0) {
return "url(#green-fill)";
} else {
return "url(#red-fill)";
}
})
This block by John Schulz successfully makes patterned bar charts that look fantastic (https://bl.ocks.org/jfsiii/7772281).
Copied the code in the below snippet for convenience and perpetuity (also added a little animation to show that it transitions well also).
var first = true;
setInterval( function(){
if(first){
d3.select('.thing-2').transition()
.delay(500)
.duration(1000)
.attr('height',20)
.attr('y',80)
}else{
d3.select('.thing-2').transition()
.delay(500)
.duration(1000)
.attr('height',100)
.attr('y',0)
}
first = !first;
},2500)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
<style>
/* FF seems to need explicit dimensions */
svg {
width: 500px;
height: 500px;
}
rect.hbar {
mask: url(#mask-stripe)
}
.thing-1 {
fill: blue;
}
.thing-2 {
fill: green;
}
</style>
</head>
<body>
<svg>
<defs>
<pattern id="pattern-stripe"
width="4" height="4"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45)">
<rect width="2" height="4" transform="translate(0,0)" fill="white"></rect>
</pattern>
<mask id="mask-stripe">
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
</mask>
</defs>
<!-- bar chart -->
<rect class="hbar thing-2" x="0" y="0" width="50" height="100"></rect>
<rect class="hbar thing-2" x="51" y="50" width="50" height="50"></rect>
<rect class="hbar thing-2" x="102" y="25" width="50" height="75"></rect>
<!-- horizontal bar chart -->
<rect class="hbar thing-1" x="0" y="200" width="10" height="50"></rect>
<rect class="hbar thing-1" x="0" y="251" width="123" height="50"></rect>
<rect class="hbar thing-1" x="0" y="302" width="41" height="50"></rect>
</svg>
</body>
</html>

Resources