获取任何图表点的坐标

时间:2017-01-16 10:36:14

标签: d3.js

我想创建一个函数谁 return the altitude of the profile with a mouseover

当我在.json中有信息时,这很容易,这里的例子是指{{d':1508,“a”:77“}。我使用这个函数:

function mousemove() {
        var x0 = x.invert(d3.mouse(this)[0]),
            i = bisectDist(data, x0, 1),
            d0 = data[i - 1],
            d1 = data[i],
            d = x0 - d0.distance > d1.distance - x0 ? d1 : d0;
        focus.attr("transform", "translate(" + x(d.distance) + "," + y(d.altitude) + ")");
        focus.select("text").text(d.altitude);
      }

但是,如果可能的话,我也希望在我有2点相距太远的情况下计算高度。例如,我有this profile for {"d": 1539, "a": 58}, {"d": 1550, "a": 158}。所以,我返回高度为d = 1539和d = 1550,但是,由于我的个人资料,我可以返回高度为d = 1546吗?

最诚挚的问候,Braz Damien。

codepen.io/Onchman/pen/dNpeaP这是codepen上的代码,我不知道如何从外部ressource添加json,所以,我试着直接在JavaScript部分添加它。

1 个答案:

答案 0 :(得分:0)

参考此answer中的代码并将其应用于您的代码,您的mousemove函数将重写为:

function mousemove() {

    var mouse = d3.mouse(this);

    var beginning = 0,
        end = areaPath.getTotalLength(),
        target = null;

    while (true){
      target = Math.floor((beginning + end) / 2);
      pos = areaPath.getPointAtLength(target);
      if ((target === end || target === beginning) && pos.x !== mouse[0]) {
          break;
      }
      if (pos.x > mouse[0])      end = target;
      else if (pos.x < mouse[0]) beginning = target;
      else break; //position found
    }

    focus.attr("transform","translate(" + mouse[0] + "," + pos.y +")");
    focus.select("text").text(y.invert(pos.y).toFixed(2));
}

完整运行代码:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    body {
      font: 10px sans-serif;
    }
    
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    
    .axis text {
      font-family: Lato;
      font-size: 13px;
      fill: black;
    }
    
    .grid path,
    .grid line {
      fill: none;
      stroke: rgba(0, 0, 0, 0.25);
      shape-rendering: crispEdges;
    }
    
    .area {
      fill: darkorange;
      stroke: rgba(0, 0, 0, 1);
    }
    
    .marker.client .marker-bg,
    .marker.client path {
      fill: rgba(255, 127, 0, 0.8);
      stroke: rgba(255, 127, 0, 0.8);
      stroke-width: 3;
    }
    
    .marker.server .marker-bg,
    .marker.server path {
      fill: rgba(0, 153, 51, 0.8);
      stroke: rgba(0, 153, 51, 0.8);
      stroke-width: 3;
    }
    
    .marker path {
      fill: none;
    }
    
    .legend text,
    .marker text {
      fill: black;
      font-weight: bold;
    }
    
    .marker text {
      text-anchor: middle;
    }
    
    .overlay {
      fill: none;
      pointer-events: all;
    }
    
    .focus circle {
      fill: none;
      stroke: steelblue;
    }
  </style>
</head>

<body>
  <script>
    function profile(rawData) {
      
      var margin = {
          top: 20,
          right: 20,
          bottom: 60,
          left: 50
        },
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom,
        innerwidth = width - margin.left - margin.right,
        innerheight = height - margin.top - margin.bottom;

      var bisectDist = d3.bisector(function(d) {
        return d.distance;
      }).left;

      var x = d3.scaleLinear().range([0, width]);

      var y = d3.scaleLinear().range([height, 0]);

      var xAxis = d3.axisBottom().scale(x);

      var yAxis = d3.axisLeft().scale(y);


      var area = d3.area().x(function(d) {
        return x(d.distance);
      }).y0(height).y1(function(d) {
        return y(d.altitude);
      })


      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.json('data.json', function(error, rawData) {
//        if (error) {
//          console.error(error);
//          return;
//        }

        var data = rawData.map(function(d) {
          return {
            altitude: d.a,
            distance: d.d
          };
        });

        //             data.sort(function(a, b) {
        //                return a.distance - b.distance;
        //                });


        x.domain(d3.extent(data, function(d) {
          return d.distance;
        }));

        y.domain([0, d3.max(data, function(d) {
          return d.altitude;
        })]);

        var x_grid = d3.axisBottom().scale(x).tickSize(-height).tickFormat("");

        var y_grid = d3.axisLeft().scale(y).tickSize(-width).tickFormat("");


        var areaPath = svg.append('svg:path')
          .datum(data)
          .attr("class", "area")
          .attr("d", area)
          .node();

        svg.append("svg:g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis)
          .append("text")
          .attr("transform", "translate(0," + margin.top * 2 + ")")
          .attr("x", width - (margin.right + margin.left))
          .text("Distance (km)");

        svg.append("svg:g")
          .attr("class", "y axis")
          .call(yAxis)
          .append("text")
          .attr("transform", "translate(70,0)")
          .attr("transform", "rotate(-90)")
          .attr("y", -45).attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Altitude (m)");

        svg.append("g")
          .attr("class", "x grid")
          .attr("transform", "translate(0," + height + ")")
          .call(x_grid);

        svg.append("g")
          .attr("class", "y grid")
          .call(y_grid);


        var focus = svg.append("g")
          .attr("class", "focus")
          .style("display", "none");

        focus.append("circle")
          .attr("r", 4.5);

        focus.append("text")
          .attr("x", 9)
          .attr("dy", ".35em");

        svg.append("rect")
          .attr("class", "overlay")
          .attr("width", width)
          .attr("height", height)
          .on("mouseover", function() {
            focus.style("display", null);
          })
          .on("mouseout", function() {
            focus.style("display", "none");
          })
          .on("mousemove", mousemove);


        function mousemove() {
          /*
          var x0 = x.invert(d3.mouse(this)[0]),
            i = bisectDist(data, x0, 1),
            d0 = data[i - 1],
            d1 = data[i],
            d = x0 - d0.distance > d1.distance - x0 ? d1 : d0;
          focus.attr("transform", "translate(" + x(d.distance) + "," + y(d.altitude) + ")");
          focus.select("text").text(d.altitude);
          */
          
          var mouse = d3.mouse(this);
          
          var beginning = 0,
                end = areaPath.getTotalLength(),
                target = null;

            while (true){
              target = Math.floor((beginning + end) / 2);
              pos = areaPath.getPointAtLength(target);
              if ((target === end || target === beginning) && pos.x !== mouse[0]) {
                  break;
              }
              if (pos.x > mouse[0])      end = target;
              else if (pos.x < mouse[0]) beginning = target;
              else break; //position found
            }
            
            focus.attr("transform","translate(" + mouse[0] + "," + pos.y +")");
            focus.select("text").text(y.invert(pos.y).toFixed(2));
          
        }



        var markerjson = [{
          "id": "1",
          "name": "Depart - Vielle-Aure",
          "type": "CP",
          "description": "Départ",
          "icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
          "distance": "1488",
          "altitude": "145"
        }, {
          "id": "2",
          "name": "CP1 - Col de Portet",
          "type": "CP",
          "description": "1er CP",
          "icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
          "distance": "1496",
          "altitude": "37"
        }, {
          "id": "3",
          "name": "CP2 - Artigues",
          "type": "CP",
          "description": "2ème CP",
          "icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
          "distance": "1504",
          "altitude": "145"
        }, {
          "id": "4",
          "name": "CP3 - Col De Sencours",
          "type": "CP",
          "description": "3ème CP",
          "icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
          "distance": "1512",
          "altitude": "137"
        }, {
          "id": "5",
          "name": "CP4 - Hautacam",
          "type": "CP",
          "description": "4ème CP",
          "icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
          "distance": "1521",
          "altitude": "45"
        }];
        
        /*
        d3.json('markersjson', function(error, markerData) {
          if (error) {
            console.error(error);
            return;
          }
        */
        
        /*
        markerData = markerjson;

          var markers = markerData.map(function(marker) {
            return {
              id: marker.id,
              name: marker.name,
              type: marker.type,
              description: marker.description,
              icon: marker.icon,
              distance: marker.distance,
            };
          });


          markers.forEach(function(marker, i) {
            setTimeout(function() {
              setItem(marker, data, svg, innerheight, x, y);
            }, 1000 + 500 * i);
          });

        */

        // });
        
      //});

    }

    function setItem(marker, data, svg, innerheight, x, y) {
      altitude = getAltitude(data, marker);
      var radius = 20,
        xPos = x(marker.distance) - radius - 3,
        yPosStart = innerheight - radius - 3,
        yPosEnd = y(altitude) - radius * 2;


      var markerG = svg.append('g')
        .attr('class', 'marker ' + marker.type.toLowerCase())
        .attr('transform', 'translate(' + xPos + ', ' + yPosStart + ')')
        .attr('opacity', 0);

      markerG.transition()
        .duration(1000)
        .attr('transform', 'translate(' + xPos + ', ' + yPosEnd + ')')
        .attr('opacity', 1);

      markerG.append("svg:image")
        .attr('class', 'marker-bg')
        .attr("xlink:href", "cp-x_15_31.png")
        .attr("x", "2")
        .attr("width", "40")
        .attr("height", "40");

      markerG.append('text')
        .attr('x', radius)
        .attr('y', radius * 0.9)

      markerG.append('text')
        .attr('x', radius)
        .attr('y', radius * 1.5)
        .attr("transform", "translate(0,15)")
        .text(marker.name);
    }



    function getAltitude(data, marker) {
      var i = 0;
      while (i < data.length) {
        if (data[i].distance == marker.distance) {
          return data[i].altitude;
        } else {
          if (i < data.length) {
            if ((data[i].distance < marker.distance) && (marker.distance < data[i + 1].distance)) {
              return ((data[i].altitude + data[i + 1].altitude) / 2)
            }
          } else {
            if ((data[i - 1].distance < marker.distance) && (marker.distance < data[i].distance)) {
              return ((data[i - 1].altitude + data[i].altitude) / 2)
            }
          }
        }
        i++;
      }
    }

    function removeItem(marker, svg, innerheight, x) {
      markerG.clear;
    }
    var profilejson = [{
      "d": 1488,
      "a": 145
    }, {
      "d": 1489,
      "a": 132
    }, {
      "d": 1490,
      "a": 70
    }, {
      "d": 1491,
      "a": 115
    }, {
      "d": 1492,
      "a": 44
    }, {
      "d": 1493,
      "a": 117
    }, {
      "d": 1494,
      "a": 9
    }, {
      "d": 1495,
      "a": 64
    }, {
      "d": 1496,
      "a": 37
    }, {
      "d": 1497,
      "a": 145
    }, {
      "d": 1498,
      "a": 14
    }, {
      "d": 1499,
      "a": 86
    }, {
      "d": 1500,
      "a": 119
    }, {
      "d": 1501,
      "a": 200
    }, {
      "d": 1502,
      "a": 23
    }, {
      "d": 1503,
      "a": 85
    }, {
      "d": 1504,
      "a": 145
    }, {
      "d": 1505,
      "a": 49
    }, {
      "d": 1506,
      "a": 145
    }, {
      "d": 1507,
      "a": 58
    }, {
      "d": 1509,
      "a": 124
    }, {
      "d": 1510,
      "a": 69
    }, {
      "d": 1511,
      "a": 14
    }, {
      "d": 1512,
      "a": 137
    }, {
      "d": 1513,
      "a": 45
    }, {
      "d": 1514,
      "a": 114
    }, {
      "d": 1515,
      "a": 186
    }, {
      "d": 1516,
      "a": 219
    }, {
      "d": 1517,
      "a": 199
    }, {
      "d": 1518,
      "a": 223
    }, {
      "d": 1519,
      "a": 28
    }, {
      "d": 1520,
      "a": 185
    }, {
      "d": 1521,
      "a": 45
    }, {
      "d": 1522,
      "a": 63
    }, {
      "d": 1523,
      "a": 18
    }, {
      "d": 1524,
      "a": 144
    }, {
      "d": 1525,
      "a": 17
    }, {
      "d": 1526,
      "a": 99
    }, {
      "d": 1527,
      "a": 214
    }, {
      "d": 1528,
      "a": 237
    }, {
      "d": 1530,
      "a": 194
    }, {
      "d": 1531,
      "a": 186
    }, {
      "d": 1532,
      "a": 19
    }, {
      "d": 1533,
      "a": 200
    }, {
      "d": 1534,
      "a": 23
    }, {
      "d": 1535,
      "a": 185
    }, {
      "d": 1536,
      "a": 45
    }, {
      "d": 1537,
      "a": 249
    }, {
      "d": 1538,
      "a": 145
    }, {
      "d": 1539,
      "a": 58
    }, {
      "d": 1550,
      "a": 158
    }];
    
    profile(profilejson);

/*
    $.ajax({
      url: 'profilejson',
      method: 'GET',
      success: function(data) {
        console.log("data =", data);
        profile(data);
      },
      error: function(data) {
        console.log("err" + data)
      }
    });
*/
  </script>
</body>

</html>