I know there's an easy solution here, but I've tried to run through a few tutorials and failed in applying them to my code. I've created an elevation chart by plotting a separate area
for each segment of 3-5 data points. So, I have quite a few plots happening on the same graph. Because of this I've tried zooming techniques that zoom on the svg
element, but have been coming up empty. Here's my latest attempt. Hopefully you guys can help.
Currently, when I try to zoom, it resets the x and y scales to [0,1] and fills the entire area with the fill color.
Here's a Plunk
// ***************************** //
// Set Up //
// ***************************** //
// *** MAP VARIABLES ***
L.mapbox.accessToken = 'pk.eyJ1Ijoid2lsbGlhbWx1Y2UiLCJhIjoiNE1zU0xMNCJ9.X9y-S0ubezlH-aefwUZslA';
var map = L.mapbox.map('map', 'examples.map-i86nkdio')
// *** CHART VARIABLES ***
var margin = {top: 10, right: 20, bottom: 30, left: 100},
width = 1100 - margin.left - margin.right,
height = 150 - margin.top - margin.bottom;
// Map colors to limits
var color = d3.scale.ordinal()
.domain([-10,-5,0,5,10])
.range(['#a1d99b','#c7e9c0','#fdd0a2','#fdae6b','#fd8d3c','#e6550d']);
// Set up the size of the chart relative to the div
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the look of the axis
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
// Define an area. Areas are filled with color.
var area = d3.svg.area()
.x(function(d) { return x(d.distance); })
.y0(height)
.y1(function(d) { return y(d.elevation); });
// Define the line
var valueline = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.distance); })
.y(function(d) { return y(d.elevation); })
// Set up the SVG element
var svg = d3.select("#chart-container")
.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 + ")")
.call(zoomed);
// Define the zoom behavior
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
// Caculate the average gradient of a dataGroup.
function dataGroupGradient(dataGroup)
{
var sum = dataGroup[0].gradient;
for (var i = 1; i < dataGroup.length; i++)
{
sum += parseFloat(dataGroup[i].gradient);
}
return sum/dataGroup.length;
}
// Define polyline options
// http://leafletjs.com/reference.html#polyline
var polyline_options = {
stroke: true,
weight: 3,
fill: true,
color: '#03f'
};
// Declare an array for holding the points that make up the path on the map
var line_points = [];
// ***************************** //
// WORKING WITH THE DATA //
// ***************************** //
// Get the data
d3.csv("first5km_Strade_Bianche.csv", function(error, data) {
data.forEach(function(d) {
d.distance = +d.distance;
d.elevation = +d.elevation;
d.gradient = +d.gradient;
d.latitude = +d.latitude;
d.longitude = +d.longitude;
line_points.push([d.latitude, d.longitude]);
});
// Scale the range of the entire chart
x.domain(d3.extent(data, function(d) { return d.distance; }));
y.domain([0, d3.max(data, function(d) { return d.elevation; })]);
// Add the overall valueline path. This path uses all of the data.
svg.append("path")
.attr("d", valueline(data));
// Split the data based on "group"
var dataGroup = d3.nest()
.key(function(d) {
return d.group;
})
.entries(data);
// To remove white space between dataGroups, append the first element of one
// dataGroup to the last element of the previous dataGroup.
dataGroup.forEach(function(group, i) {
if(i < dataGroup.length - 1) {
group.values.push(dataGroup[i+1].values[0])
}
})
// Draw the array of line_points to the map and fit the bounds.
var polyline = L.polyline(line_points, polyline_options).addTo(map);
map.fitBounds(polyline.getBounds());
// Add a line and an area for each dataGroup
dataGroup.forEach(function(d, i){
svg.append("path")
.datum(d.values)
.attr("class", "area")
.attr("d", area);
});
// Fill the dataGroups with color
svg.selectAll(".area")
.style("fill", function(d) { return color(dataGroupGradient(d)); });
// 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);
// Add the text label for the X axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height+margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Distance");
// Add the text label for the Y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("x", margin.top - (height / 2))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
});
// *********************** //
// Zoom specific updates //
// *********************** //
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll('path.line').attr('d', line);
}
D3's documentation of scales being attached to the zoom behaviour specifies:
You need to defer binding of your scales to the zoom listener until the domains of the scales are set:
After data has been loaded and the scales' domain values have been set accordingly you may bind the scales to the listener and after that attach it to your
svg
:Please check the adjusted plunker to see if this solves your problem.