2
votes

I want to create a chart that will hide the overflow of a line chart when I have a max and min values for the Y axis. For example I have a min value of 200. I modified the domain for the Y axis from y.domain([0, d3.max(dataSet, function (d) { return d.value; })]); to y.domain([200, d3.max(dataSet, function (d) { return d.value; })]);. However if it has enough space in the SVG element, below the X axis, it will start below it. I've seen in some other posts that the forceY() function should be used, but as I understand it is only for NVD3... Also, playing with data, and removing the data points that are under the specified level, is not a solution.

var dataSet = [{ date: '2013-01', value: '53' }, { date: '2013-02', value: '165' }, { date: '2013-03', value: '269' }, 
               { date: '2013-04', value: '344' }, { date: '2013-05', value: '376' }, { date: '2013-06', value: '410' }, 
               { date: '2013-07', value: '421' }, { date: '2013-08', value: '405' }, { date: '2013-09', value: '376' }, 
               { date: '2013-10', value: '359' }, { date: '2013-11', value: '392' }, { date: '2013-12', value: '433' }, 
               { date: '2014-01', value: '455' }, { date: '2014-02', value: '478' }, { date: '2014-03', value: '455' }, 
               { date: '2014-04', value: '500' }];

		// Set the dimensions of the canvas / graph
		var margin = {top: 30, right: 20, bottom: 40, left: 50},
			width = 600 - margin.left - margin.right,
			height = 370 - margin.top - margin.bottom;
		
		// Parse the date / time
		var parseDate = d3.time.format("%Y-%m").parse;
		
		// Set the ranges
		var x = d3.time.scale().range([0, width]);
		var y = d3.scale.linear().range([height, 0]);
		
		// Define the axes
		var xAxis = d3.svg.axis().scale(x)
			.orient("bottom").tickFormat(d3.time.format("%m"));
		var yAxis = d3.svg.axis().scale(y)
			.orient("left").ticks(5);
		
		// Define the line
		var valueline = d3.svg.line()
			.interpolate("basis") //smoothens the line
			.x(function(d) { return x(d.date); })
			.y(function(d) { return y(d.value); });
		
		// Adds the svg canvas
		var svg = d3.select("body")
			.append("svg")
				.attr("width", width + margin.left + margin.right)
				.attr("height", height + margin.top + margin.bottom)
				.attr("id", "svg01")
			.append("g")
			.	attr("transform",
					"translate(" + margin.left + "," + margin.top + ")");
		
		// Get the data
		dataSet.forEach(function (d) {
			d.date = parseDate(d.date);
			d.value = +d.value;
		});

		// Scale the range of the data
		x.domain(d3.extent(dataSet, function (d) { return d.date; }));
		y.domain([200, d3.max(dataSet, function (d) { return d.value; })]);

		// Add the valueline path.
		svg.append("path")
			.attr("class", "line")
			.attr("d", valueline(dataSet));

		// Add the X Axis
		svg.append("g")
			.attr("class", "x axis")
			.attr("transform", "translate(0," + height + ")")
			.call(xAxis);

		// Add the Y Axis
		svg.append("g")
			.attr("class", "y axis")
			.call(yAxis);
body {
  font: 12px Arial;
}
path {
  stroke: steelblue;
  stroke-width: 2;
  fill: none;
}
.axis path,
.axis line {
  fill: none;
  stroke: grey;
  stroke-width: 1;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
1
What you're looking for is to apply clamping to your scale. By default this is disabled and produces the behaviour you're seeing. Essentially, the default scale is allowed to extrapolate beyond the given paramaters. Enabling paramaters prevents this extrapolation, and treats the domain/range as an interval of valid input.JSBob
@JSbob it is still displaying the line on the X axis... With C3 for example, the line is cut at the bottom. In this example: c3js.org/samples/axes_y_range.html if we change the "min" from -400 to 100, it will cut the line of the chart somewhere between 0 and 1 on the X axis. Is that possible with d3?John

1 Answers

3
votes

You need to apply a clipPath:

var dataSet = [{ date: '2013-01', value: '53' }, { date: '2013-02', value: '165' }, { date: '2013-03', value: '269' }, 
               { date: '2013-04', value: '344' }, { date: '2013-05', value: '376' }, { date: '2013-06', value: '410' }, 
               { date: '2013-07', value: '421' }, { date: '2013-08', value: '405' }, { date: '2013-09', value: '376' }, 
               { date: '2013-10', value: '359' }, { date: '2013-11', value: '392' }, { date: '2013-12', value: '433' }, 
               { date: '2014-01', value: '455' }, { date: '2014-02', value: '478' }, { date: '2014-03', value: '455' }, 
               { date: '2014-04', value: '500' }];

		// Set the dimensions of the canvas / graph
		var margin = {top: 30, right: 20, bottom: 40, left: 50},
			width = 600 - margin.left - margin.right,
			height = 370 - margin.top - margin.bottom;
		
		// Parse the date / time
		var parseDate = d3.time.format("%Y-%m").parse;
		
		// Set the ranges
		var x = d3.time.scale().range([0, width]);
		var y = d3.scale.linear().range([height, 0]);
		
		// Define the axes
		var xAxis = d3.svg.axis().scale(x)
			.orient("bottom").tickFormat(d3.time.format("%m"));
		var yAxis = d3.svg.axis().scale(y)
			.orient("left").ticks(5);
		
		// Define the line
		var valueline = d3.svg.line()
			.interpolate("basis") //smoothens the line
			.x(function(d) { return x(d.date); })
			.y(function(d) { return y(d.value); });
		
		// Adds the svg canvas
		var svg = d3.select("body")
			.append("svg")
				.attr("width", width + margin.left + margin.right)
				.attr("height", height + margin.top + margin.bottom)
				.attr("id", "svg01")
			.append("g")
			.	attr("transform",
					"translate(" + margin.left + "," + margin.top + ")");

// ADDED THIS ////////////////////////
svg.append("defs").append("clipPath")
.attr("id", "clip")
  .append("rect")
.attr("width", width)
.attr("height", height);
/////////////////////////////////////
		
		// Get the data
		dataSet.forEach(function (d) {
			d.date = parseDate(d.date);
			d.value = +d.value;
		});

		// Scale the range of the data
		x.domain(d3.extent(dataSet, function (d) { return d.date; }));
		y.domain([200, d3.max(dataSet, function (d) { return d.value; })]);

		// Add the valueline path.
		svg.append("path")
// AND THIS ////////////////////////////////
.attr("clip-path", "url(#clip)")
///////////////////////////////////////////
			.attr("class", "line")
			.attr("d", valueline(dataSet));

		// Add the X Axis
		svg.append("g")
			.attr("class", "x axis")
			.attr("transform", "translate(0," + height + ")")
			.call(xAxis);

		// Add the Y Axis
		svg.append("g")
			.attr("class", "y axis")
			.call(yAxis);
body {
  font: 12px Arial;
}
path {
  stroke: steelblue;
  stroke-width: 2;
  fill: none;
}
.axis path,
.axis line {
  fill: none;
  stroke: grey;
  stroke-width: 1;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>