[英]Draw arrow inside a datamaps using d3.js
我是d3.js 的新手,我正在尝试创建自定义地图 svg 。
通过一些参考,我做了一个自定义地图,如下所示。
上面输出的代码片段在这里。
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(" + px + "," + py + ") " + 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>
我有一个附着在身体上的标记,但我需要的实际输出是,
- 箭头必须从图像中显示的气泡开始
- 它应该以一些随机方向结束,以便可以添加一个弹出模板框来描述实际位置。
所以最后我需要的实际输出应该看起来像这样。
任何帮助表示赞赏。
可以通过在数据中包含 2 个字段来单独自定义线: arrowDirectionAngle
和arrowLineLength
。
"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(" + px + "," + py + ") " + 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>
使用路径线和箭头分别添加线条,并使用鼠标事件您可以根据悬停气泡的中心操纵路径的位置,对于工具提示,您可以使用d3.select()
来获取工具提示的div
和更改其innerHTML
属性以显示消息。
这是一个有效的解决方案:
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); } // 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) { return function(t) { var p = path.getPointAtLength(t * l); if(t < 0.111) { return ''; } return "translate(" + px + "," + py + ") " + 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" }, { 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 ``; // return `<div class="hoverinfo">city: ${data.state}, Slums: ${data.radius}%</div>`; } }); const line_data = [{source: {x:0, y:0}, target: {x:100, y:100}}]; var path = d3.select('.datamap').append("path") .data(line_data) .attr("class", "line") .attr("d", lineData); var arrow = d3.select('.datamap').append("svg:path") .attr('d', null) .attr('class', 'tri'); }, 1000); setTimeout(() => { // only start drawing bubbles on the map when map has rendered completely. const svg = d3.select('.datamap'); var circles = d3.selectAll('circle'); var state_data = d3.selectAll('circle').data(); circles.on("mouseover", function(d, i) { if(!document.querySelectorAll(".active").length) { let x = circles[0][i].cx.baseVal.value; let y = circles[0][i].cy.baseVal.value+8; state_info = state_data[i]; const line_data = [{source: {x, y}, target: {x , y : y + 100}}]; if(i === 2) { line_data[0].source.x = line_data[0].source.x - 8; line_data[0].source.y = line_data[0].source.y - 8; line_data[0].target.y = line_data[0].source.y; line_data[0].target.x = line_data[0].source.x - 150; } if(i === 3) { line_data[0].source.x = line_data[0].source.x + 8; line_data[0].source.y = line_data[0].source.y - 8; line_data[0].target.y = line_data[0].source.y; line_data[0].target.x = line_data[0].source.x + 100; } if(i === 4) { line_data[0].source.x = line_data[0].source.x - 8; line_data[0].source.y = line_data[0].source.y - 8; line_data[0].target.y = line_data[0].source.y + 50; line_data[0].target.x = line_data[0].source.x / 2; } if(i === 5) { line_data[0].source.x = line_data[0].source.x + 8; line_data[0].source.y = line_data[0].source.y - 8; line_data[0].target.y = line_data[0].source.y; line_data[0].target.x = line_data[0].source.x + 100; } var path = d3.select('path.line'); path.data(line_data) .attr("class", "line") .attr("d", lineData); var arrow = d3.select('.tri'); arrow.attr("d", d3.svg.symbol().type("triangle-down")(10,1)); arrow.interrupt(); arrow.transition() .duration(2000) .ease("linear") .attr("class", "tri") .attrTween("transform", translateAlong(path.node())) .each("end", () => { d3.select('.datamaps-hoverover') .style('display','block') .style('left', (line_data[0].target.x + 10) + "px") .style('top', (line_data[0].target.y + 10) +"px") .html('<div class="hoverinfo"><strong> this is from the custom tooltip: city: ' + state_info.state + ', Slum: ' + state_info.radius + '%</strong></div>'); }); var totalLength = path.node().getTotalLength(); path.interrupt(); path .attr("stroke-dasharray", totalLength + " " + totalLength) .attr("stroke-dashoffset", totalLength) .transition() .duration(2000) .ease("linear") .attr("stroke-dashoffset", 0); } }); circles.on("mouseout", function(d, i) { d3.select('path.line').attr('d', null); d3.select('path.tri').attr('d', null); d3.select('.datamaps-hoverover') .style('display','none') .html(''); }) }, 1500);
.line { stroke: blue !important; fill: blue } .tri { stroke: blue !important; fill: blue } circle { fill: red; } #marker { stroke: black; fill: black; }
<!DOCTYPE html> <meta charset="utf-8"> <html> <head> <script src="https://d3js.org/d3.v3.min.js"></script> <script src="https://d3js.org/topojson.v1.min.js"></script> <script src="https://rawgit.com/Anujarya300/bubble_maps/master/data/geography-data/datamaps.none.js"></script> </head> <body> <div id="canada" style="height: 600px; width: 900px;"></div> </body> </html>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.