javascript - 使用 d3.js 在数据映射中绘制箭头

标签 javascript html d3.js svg datamaps

我是 d3.js 的新手,我正在尝试创建自定义 map svg。

根据一些引用,我做了一个自定义 map ,如下所示。

Arrow pointed at the bottom left of the page

上面输出的代码片段在这里。

https://jsfiddle.net/9kbp4h6j/

"use strict"

var svg = d3.select("body").append("svg").append("g").attr("transform", "translate(100,50)")

svg.append("svg:defs")
  .append("svg:marker")
  .attr("id", "arrow")
  .attr("refX", 2)
  .attr("refY", 6)
  .attr("markerWidth", 13)
  .attr("markerHeight", 13)
  .attr("orient", "auto")
  .append("svg:path")
  .attr("d", "M2,2 L2,11 L10,6 L2,2");


var line = d3.svg.line()
  .x(function (point) {
    return point.lx;
  })
  .y(function (point) {
    return point.ly;
  });

function lineData(d) {
  // i'm assuming here that supplied datum 
  // is a link between 'source' and 'target'
  var points = [{
      lx: d.source.x,
      ly: d.source.y
    },
    {
      lx: d.target.x,
      ly: d.target.y
    }
  ];
  return line(points);
}

var path = svg.append("path")
  .data([{
    source: {
      x: 0,
      y: 0
    },
    target: {
      x: 80,
      y: 80
    }
  }])
  .attr("class", "line")
  //.style("marker-end", "url(#arrow)")
  .attr("d", lineData);
//var arrow = svg.append("svg:path")
//.attr("d", "M2,2 L2,11 L10,6 L2,2");


console.log(d3.svg.symbol())

var arrow = svg.append("svg:path")
  .attr("d", d3.svg.symbol().type("triangle-down")(10, 1));


arrow.transition()
  .duration(2000)
  .ease("linear")
  .attrTween("transform", translateAlong(path.node()))
//.each("end", transition);


// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();
  var ps = path.getPointAtLength(0);
  var pe = path.getPointAtLength(l);
  var angl = Math.atan2(pe.y - ps.y, pe.x - ps.x) * (180 / Math.PI) - 90;
  var rot_tran = "rotate(" + angl + ")";
  return function (d, i, a) {
    console.log(d);

    return function (t) {
      var p = path.getPointAtLength(t * l);
      return "translate(" + p.x + "," + p.y + ") " + rot_tran;
    };
  };
}

var totalLength = path.node().getTotalLength();

path
  .attr("stroke-dasharray", totalLength + " " + totalLength)
  .attr("stroke-dashoffset", totalLength)
  .transition()
  .duration(2000)
  .ease("linear")
  .attr("stroke-dashoffset", 0);


var bubble_map = new Datamap({
  element: document.getElementById('canada'),
  scope: 'canada',
  geographyConfig: {
    popupOnHover: true,
    highlightOnHover: true,
    borderColor: '#444',
    borderWidth: 0.5,
    dataUrl: 'https://rawgit.com/Anujarya300/bubble_maps/master/data/geography-data/canada.topo.json'
    //dataJson: topoJsonData
  },
  fills: {
    'MAJOR': '#306596',
    'MEDIUM': '#0fa0fa',
    'MINOR': '#bada55',
    defaultFill: '#dddddd'
  },
  data: {
    'JH': {
      fillKey: 'MINOR'
    },
    'MH': {
      fillKey: 'MINOR'
    }
  },
  setProjection: function (element) {
    var projection = d3.geo.mercator()
      .center([-106.3468, 68.1304]) // always in [East Latitude, North Longitude]
      .scale(250)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

    var path = d3.geo.path().projection(projection);
    return {
      path: path,
      projection: projection
    };
  }
});

let bubbles = [{
    centered: "MB",
    fillKey: "MAJOR",
    radius: 8,
    state: "Manitoba"
  },
  {
    centered: "AB",
    fillKey: "MAJOR",
    radius: 8,
    state: "Alberta"
  },
  {
    centered: "NT",
    fillKey: "MAJOR",
    radius: 8,
    state: "Northwest Territories"
  },
  {
    centered: "NU",
    fillKey: "MEDIUM",
    radius: 8,
    state: "Nunavut"
  },
  {
    centered: "BC   ",
    fillKey: "MEDIUM",
    radius: 8,
    state: "British Columbia"
  },
  {
    centered: "QC",
    fillKey: "MINOR",
    radius: 8,
    state: "Québec"
  },
  {
    centered: "NB",
    fillKey: "MINOR",
    radius: 8,
    state: "New Brunswick"
  }

]
// // ISO ID code for city or <state></state>
setTimeout(() => { // only start drawing bubbles on the map when map has rendered completely.
  bubble_map.bubbles(bubbles, {
    popupTemplate: function (geo, data) {
      return `<div class="hoverinfo">city: ${data.state}, Slums: ${data.radius}%</div>`;
    }
  });
}, 1000);
.line {
  stroke: blue;
  stroke-width: 1.5px;
  fill: white;
}

circle {
  fill: red;
}
#marker {
 stroke: black;
 fill: black;
}
<!DOCTYPE html>
<html>
  <meta charset="utf-8">
  <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="http://d3js.org/topojson.v1.min.js"></script>
    <script src="https://rawgit.com/Anujarya300/bubble_maps/master/data/geography-data/datamaps.none.js"></script>
    <div id="canada" style="height: 600px; width: 900px;"></div>
  </body>
</html>

我有一个附在 body 上的标记,但我需要的实际输出是,

  1. The arrow must be starting from the bubble shown in the image
  2. It should end on some random directions so that a popup template box can be added to describe the actual location.

所以最后我需要的实际输出应该看起来有点像这样。

Arrow pointed out from each and every location

感谢任何帮助。

最佳答案

可以通过在数据中包含 2 个字段来单独自定义线条:arrowDirectionAnglearrowLineLength

"use strict"

var line = d3.svg.line()
    .x(function (point) {
        return point.lx;
    })
    .y(function (point) {
        return point.ly;
    });

function lineData(d) {
    // i'm assuming here that supplied datum 
    // is a link between 'source' and 'target'
    var points = [{
        lx: d.source.x,
        ly: d.source.y
    },
    {
        lx: d.target.x,
        ly: d.target.y
    }
    ];
    return line(points);
}

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
    var l = path.getTotalLength();
    var ps = path.getPointAtLength(0);
    var pe = path.getPointAtLength(l);
    var angl = Math.atan2(pe.y - ps.y, pe.x - ps.x) * (180 / Math.PI) - 90;
    var rot_tran = "rotate(" + angl + ")";
    return function (d, i, a) {
        //console.log(d);

        return function (t) {
            var p = path.getPointAtLength(t * l);
            return "translate(" + p.x + "," + p.y + ") " + rot_tran;
        };
    };
}

var bubble_map = new Datamap({
    element: document.getElementById('canada'),
    scope: 'canada',
    geographyConfig: {
        popupOnHover: true,
        highlightOnHover: true,
        borderColor: '#444',
        borderWidth: 0.5,
        dataUrl: 'https://rawgit.com/Anujarya300/bubble_maps/master/data/geography-data/canada.topo.json'
        //dataJson: topoJsonData
    },
    fills: {
        'MAJOR': '#306596',
        'MEDIUM': '#0fa0fa',
        'MINOR': '#bada55',
        defaultFill: '#dddddd'
    },
    data: {
        'JH': {
            fillKey: 'MINOR'
        },
        'MH': {
            fillKey: 'MINOR'
        }
    },
    setProjection: function (element) {
        var projection = d3.geo.mercator()
            .center([-106.3468, 68.1304]) // always in [East Latitude, North Longitude]
            .scale(250)
            .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

        var path = d3.geo.path().projection(projection);
        return {
            path: path,
            projection: projection
        };
    }
});

let bubbles = [{
    centered: "MB",
    fillKey: "MAJOR",
    radius: 8,
    state: "Manitoba",
    arrowDirectionAngle: 90,
    arrowLineLength: 120
},
{
    centered: "AB",
    fillKey: "MAJOR",
    radius: 8,
    state: "Alberta",
    arrowDirectionAngle: 90,
    arrowLineLength: 100
},
{
    centered: "NT",
    fillKey: "MAJOR",
    radius: 8,
    state: "Northwest Territories",
    arrowDirectionAngle: 180,
    arrowLineLength: 130
},
{
    centered: "NU",
    fillKey: "MEDIUM",
    radius: 8,
    state: "Nunavut",
    arrowDirectionAngle: -25,
    arrowLineLength: 80
},
{
    centered: "BC   ",
    fillKey: "MEDIUM",
    radius: 8,
    state: "British Columbia",
    arrowDirectionAngle: 125,
    arrowLineLength: 65
},
{
    centered: "QC",
    fillKey: "MINOR",
    radius: 8,
    state: "Québec",
    arrowDirectionAngle: -25,
    arrowLineLength: 70
},
{
    centered: "NB",
    fillKey: "MINOR",
    radius: 8,
    state: "New Brunswick",
    arrowDirectionAngle: 65,
    arrowLineLength: 50
}

]


function renderArrows(targetElementId) {
    let svgRoot = d3.select("#" + targetElementId).select("svg");

    svgRoot.append("svg:defs")
        .append("svg:marker")
        .attr("id", "arrow")
        .attr("refX", 2)
        .attr("refY", 6)
        .attr("markerWidth", 13)
        .attr("markerHeight", 13)
        .attr("orient", "auto")
        .append("svg:path")
        .attr("d", "M2,2 L2,11 L10,6 L2,2");

    let linesGroup = svgRoot.append("g");

    linesGroup.attr("class", "lines");

    let bubbleElements = svgRoot.selectAll(".datamaps-bubble")[0];

    bubbleElements.forEach(function (bubbleElement) {
        let xPosition = bubbleElement.cx.baseVal.value;
        let yPosition = bubbleElement.cy.baseVal.value;
        let datum = d3.select(bubbleElement).datum();

        let degree = datum.arrowDirectionAngle;
        let radius = datum.arrowLineLength;
        let theta = degree * Math.PI / 180;

        let path = linesGroup.append("path")
            .data([{
                source: {
                    x: xPosition,
                    y: yPosition
                },
                target: {
                    x: xPosition + radius * Math.cos(theta),
                    y: yPosition + radius * Math.sin(theta)
                }
            }])
            .style("stroke", "blue")
            .style("stroke-width", "1.5px")
            .style("fill", "white")
            //.style("marker-end", "url(#arrow)")
            .attr("d", lineData);


        let arrow = svgRoot.append("svg:path")
            .attr("d", d3.svg.symbol().type("triangle-down")(10, 1));


        arrow.transition()
            .duration(2000)
            .ease("linear")
            .attrTween("transform", translateAlong(path.node()))

        var totalLength = path.node().getTotalLength();

        path
            .attr("stroke-dasharray", totalLength + " " + totalLength)
            .attr("stroke-dashoffset", totalLength)
            .transition()
            .duration(2000)
            .ease("linear")
            .attr("stroke-dashoffset", 0);

    });
}

// // ISO ID code for city or <state></state>
setTimeout(() => { // only start drawing bubbles on the map when map has rendered completely.
    bubble_map.bubbles(bubbles, {
        popupTemplate: function (geo, data) {
            return `<div class="hoverinfo">city: ${data.state}, Slums: ${data.radius}%</div>`;
        }
    });

    renderArrows("canada");

}, 1000);
.line {
    stroke: blue;
    stroke-width: 1.5px;
    fill: white;
}

circle {
    fill: red;
}

#marker {
    stroke: black;
    fill: black;
}
<!DOCTYPE html>
<html>
  <meta charset="utf-8">
  <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="http://d3js.org/topojson.v1.min.js"></script>
    <script src="https://rawgit.com/Anujarya300/bubble_maps/master/data/geography-data/datamaps.none.js"></script>
    <div id="canada" style="height: 600px; width: 900px;"></div>
  </body>
</html>

关于javascript - 使用 d3.js 在数据映射中绘制箭头,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60144871/

相关文章:

javascript - 可以用我自己的自定义图像替换光标吗?

javascript - jquery 克隆不创建唯一对象

html - ol/ul 应该在 <p> 内部还是外部?

javascript - 类型错误 : Cannot read property 'style' of null

javascript - 缩放 D3 Hexbin map 示例以适合 div

d3.js - 如何在 ViewBox 中使用 D3 缩放行为而不是变换

javascript - 这个高阶函数有什么问题?

python - 如何使用 Flask 和 Jinja2 模板更改 HTML 属性?

javascript - D3 : Highlighting Parts of Path on Mouseover in Line Graph

javascript - 如何以编程方式告诉 HTML SELECT 下拉(例如,由于鼠标悬停)?