Svg clip-path within rectangle does not work - svg

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.

Related

Scaling Error/Axis Error in D3.js

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>

d3.js - Zoom and Center on Click - Map scales, points do not

I'm adapting the zoomable and clickable map found http://bl.ocks.org/mbostock/2206340 at to plot some points and do some other things. Right now, I'm trying to make it such that on the zoom and click actions, the plotted points also move / honor the zoom. I'm not sure what in the code here is wrong, since I seem to be calling the red.circle and blue.circle objects in the zoom + click -- can anyone identify the issue? Thanks! data.csv is formatted as follows:
lon_0,lat_0,lon_1,lat_1
-122.1430195,37.4418834,-122.415278,37.778643
-122.1430195,37.4418834,-122.40815,37.785034
-122.4194155,37.7749295,-122.4330827,37.7851673
-122.4194155,37.7749295,-122.4330827,37.7851673
-118.4911912,34.0194543,-118.3672828,33.9164666
-121.8374777,39.7284944,-121.8498415,39.7241178
-115.172816,36.114646,-115.078011,36.1586877
and here is the d3.js script.
.background {
fill: none;
pointer-events: all;
}
#states path {
fill: #aaa;
stroke: #fff;
stroke-width: 1.5px;
}
#states path:hover {
stroke: white;
}
</style>
<body>
<script>
var width = 1920/2,
height = 1000/2;
var projection = d3.geo.albersUsa()
.scale(width)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scale(projection.scale())
.scaleExtent([height, 50 * height])
.on("zoom", zoom);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "stroke:black; stroke-width:2px");
var states = svg.append("g")
.attr("id", "states")
.call(zoom);
var dataset = [];
states.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
d3.json("us-states.json", function(json) {
states.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path)
.on("click", click);
d3.csv("data.csv", function(data) {
states.selectAll(".blue.circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d["lon_0"], d["lat_0"] ])[0];
})
.attr("cy", function(d) {
return projection([d["lon_0"],d["lat_0"] ])[1];
})
.attr("r", 5)
.attr("class", "blue circle")
.style("fill", "blue");
states.selectAll(".red.circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([+d["lon_1"], +d["lat_1"] ])[0];
})
.attr("cy", function(d) {
return projection([+d["lon_1"],+d["lat_1"] ])[1];
})
.attr("r", 5)
.attr("class", "red circle")
.style("fill", "red");
});
});
function click(d) {
var centroid = path.centroid(d),
translate = projection.translate();
projection.translate([
translate[0] - centroid[0] + width / 2,
translate[1] - centroid[1] + height / 2
]);
zoom.translate(projection.translate());
states.selectAll("path").transition()
.duration(1000)
.attr("d", path);
states.selectAll("red.circle").transition()
.duration(1000)
.attr("d", circle);
states.selectAll("blue.circle").transition()
.duration(1000)
.attr("d", circle);
}
function zoom() {
projection.translate(d3.event.translate).scale(d3.event.scale);
states.selectAll("path").attr("d", path);
states.selectAll("red.circle").attr("d", path);
states.selectAll("blue.circle").attr("d",path);
}
</script>
you're setting the co-ordinates of the circles when you load the map, so when you click the zoom function, your circles are displayed but are not using the same co-ordinates - i think - it will help if you can create a http://bl.ocks.org to see this.
perhaps this could be of help http://bl.ocks.org/nkhine/3150901 only UK, US and Afganistan works, but i am basically re-projecting the secondary map to fit the new zoom level.

Gradient colors from d3.scale.category10() with opacity change on a svg circle?

I am trying to apply the colors from the color = d3.scale.category10(); var to the gradient for the circle svg, what am I doing wrong? All I am seeing is the first color of the color = d3.scale.category10();(which is blue) to 0% opacity gradient but that is all. If I take the gradient out then I see the range I want which is from 1-4? Thanks in advance!
var nodes = d3.range(300).map(function() { return {radius: Math.random() * 12 + 4}; }),
root = nodes[0],
color = d3.scale.category10();
root.radius = 0;
root.fixed = true;
var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) { return i ? 0 : -4000; })
.nodes(nodes)
.size([width, height]);
force.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height);
var gradient = svg.append("defs").append("radialGradient")
.attr("id", "gradient")
.attr("cx", "50%")
.attr("cy", "50%");
gradient.append("stop")
.attr("offset", "75%")
.style("stop-color", function(d, i) { return color(i % 4); })
.attr("stop-opacity", "1");
gradient.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) { return color(i % 4); })
.attr("stop-opacity", ".1");
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", "url(#gradient)");
Your stop elements don't have any data joined with them, so in your function (d, i), i will always be 0. If you just want the two stops, you could do something like this:
gradient.append("stop")
.attr("offset", "75%")
.style("stop-color", color(0))
.attr("stop-opacity", "1");
gradient.append("stop")
.attr("offset", "100%")
.style("stop-color", color(1))
.attr("stop-opacity", ".1");
If instead you're just trying to fade the edges of your circles, a gradient isn't what you want at all. Instead, you'll need to apply a solid color to each circle, then create a single opacity-only gradient inside a mask, and apply that mask to each circle. Something like this:
var defs = svg.append('defs');
var gradient = defs.append('radialGradient')
.attr('id', 'fadient');
gradient.append('stop')
.attr('offset', '75%')
.attr('stop-color', 'white')
.attr('stop-opacity', 1)
gradient.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'white')
.attr('stop-opacity', .1)
var mask = defs.append('mask')
.attr('id', 'mask')
.attr('maskContentUnits', 'objectBoundingBox')
.append('circle')
.attr('fill', 'url(#fadient)')
.attr('cx', .5)
.attr('cy', .5)
.attr('r', .5)
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr('cx', function (d, i) { return 20 * i })
.attr('cy', 50)
.attr("r", function(d) { return d.radius; })
.attr('mask', 'url(#mask)')
.attr("fill", function (d, i) { return color(i); });

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.

Resources