admin管理员组

文章数量:1398840

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 ing 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
// .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);   
}

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 ing 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./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);   
}
Share edited Mar 24, 2015 at 14:05 Will Luce asked Mar 23, 2015 at 3:37 Will LuceWill Luce 1,8313 gold badges23 silver badges36 bronze badges 2
  • Your plunk is not working because it is missing the input file first5km_Strade_Bianche.csv. Furthermore, you are selecting d3.select("#chart-container") without having an element with id=chart-container. Please correct your plunk. – altocumulus Commented Mar 24, 2015 at 15:26
  • Not sure why that happened. Must have grabbed the link before saving. It's good now. – Will Luce Commented Mar 24, 2015 at 15:28
Add a ment  | 

1 Answer 1

Reset to default 5

D3's documentation of scales being attached to the zoom behaviour specifies:

# zoom.x([x])

… If the scale's domain or range is modified programmatically, this function should be called again.

You need to defer binding of your scales to the zoom listener until the domains of the scales are set:

// create the zoom listener
var zoomListener = d3.behavior.zoom()
//    Defer binding of scales until domains have been set.
//    .x(x)  
//    .y(y)
    .scaleExtent([1, 10])
    .on("zoom", zoomHandler);

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:

// 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; })]);
svg.call(zoomListener.x(x).y(y));

Please check the adjusted plunker to see if this solves your problem.

本文标签: javascriptD3 Zooming not workingStack Overflow