how to plot the image inside the polygon in d3 - javascript
hi all i am using d3 chart with polygon i have one map structure d3 chart and plot one circle for the purpose of show tooltip now my need is i need to show one image 'https://i.stack.imgur.com/O9xB5.png' to replace the circle so when mouse over the image i shown tooltip and another need show 'State Abbr' inside polygon like Ak,TD,PD...
.help ow to do this here i attached my code files
code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
/* Style for Custom Tooltip */
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* Legend Font Style */
body {
font: 11px sans-serif;
}
/* Legend Position Style */
.legend {
position:absolute;
left:800px;
top:350px;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height of map
var width = 960;
var height = 500;
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([width/2, height/2])
.scale([1000]);
// Define path generator
var path = d3.geo.path()
.projection(projection);
// Define linear scale for output
var color = d3.scale.linear()
.range(["green","red"]);
var legendText = ["Population Present", "Population Absent"];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Load in my states data!
d3.csv("Population_education.csv", function(data) {
// Load GeoJSON data and merge with states data
d3.json("us-states.json", function(json) {
// Loop through each state data value in the .csv file
for (var i = 0; i < data.length; i++) {
// Grab State Name
var dataState = data[i].SiteState;
// Get Population
var dataPop = data[i].Population;
// Grab data value
if(data[i].Members > 0) {
var dataValue = 1;
}
else { var dataValue = 0;}
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.MembersPresent = dataValue;
json.features[j].properties.pop = +dataPop;
// Stop looking through the JSON
break;
}
}
}
// Get Max and Min Population and update colorscale
var max = d3.max(json.features, function(d) { return d.properties.pop });
var min = d3.min(json.features, function(d) { return d.properties.pop })
color.domain([min, max]); // setting the range of the input data
// Bind the data to the SVG and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1")
.style("fill", function(d) {
return color(d.properties.pop)
});
// Map the cities I have lived in!
d3.csv("Population_education.csv", function(data) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
if (d.AvgLng != 0 && d.AvgLat != 0)
return projection([d.AvgLng, d.AvgLat])[0];
})
.attr("cy", function(d) {
if (d.AvgLng != 0 && d.AvgLat != 0)
return projection([d.AvgLng, d.AvgLat])[1];
})
.attr("r", function(d) {
return 3;
})
.style("fill", "rgb(217,91,67)")
.style("opacity", 0.45)
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("State:" + d['State Abbr'] + "<br/>" + "Pop:" + d.Population)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 140)
.attr("height", 200)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.data(legendText)
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
});
</script>
</body>
</html>
Data
Population_education.csv
RowID,SiteState,State Abbr,AvgLat,AvgLng,Population
1,Alabama,AL,32.806671,-86.79113,28
2,Arizona,AZ,33.729759,-111.431221,11704
3,California,CA,36.116203,-119.681564,4356448
4,Colorado,CO,39.059811,-105.311104,374435
5,Connecticut,CT,41.597782,-72.755371,455966
6,Florida,FL,27.766279,-81.68678300000001,442537
7,Georgia,GA,33.040619,-83.643074,1339081
8,Illinois,IL,40.349457,-88.986137,29
9,Indiana,IN,39.849426,-86.258278,1525124
10,Iowa,IA,42.011539,-93.210526,185146
11,Kansas,KS,38.5266,-96.72648599999999,129301
12,Kentucky,KY,37.66814,-84.670067,621047
13,Louisiana,LA,31.169546,-91.867805,170568
14,Maine,ME,44.693947,-69.381927,222966
15,Maryland,MD,39.063946,-76.80210099999999,256966
16,Massachusetts,MA,42.230171,-71.530106,27
17,Michigan,MI,43.326618,-84.536095,27
18,Minnesota,MN,45.694454,-93.900192,11
19,Missouri,MO,38.456085,-92.28836800000001,420415
20,Nevada,NV,38.313515,-117.055374,309799
21,New Hampshire,NH,43.452492,-71.563896,195948
22,New Jersey,NJ,40.298904,-74.521011,241039
23,New Mexico,NM,34.840515,-106.248482,1945
24,New York,NY,42.165726,-74.94805100000001,1075153
25,North Carolina,NC,35.630066,-79.80641900000001,14
26,Ohio,OH,40.388783,-82.764915,1526404
27,Oregon,OR,44.572021,-122.070938,11
28,Pennsylvania,PA,40.590752,-77.209755,197
29,South Carolina,SC,33.856892,-80.945007,45
30,Tennessee,TN,35.747845,-86.692345,446667
31,Texas,TX,31.054487,-97.563461,736672
32,Vermont,VA,37.769337,-78.169968,2324640
33,Washington,WA,47.400902,-121.490494,141319
34,West Virginia,WV,38.491226,-80.954453,128275
35,Wisconsin,WI,44.268543,-89.616508,405942
36,Alaska,AK,0,0,0
37,Arkansas,AR,0,0,0
38,Delaware,DE,0,0,0
39,District of Columbia,DC,0,0,0
40,Hawaii,HI,0,0,0
41,Idaho,ID,0,0,0
42,Mississippi,MS,0,0,0
43,Montana,MT,0,0,0
44,Nebraska,NE,0,0,0
45,North Dakota,ND,0,0,0
46,South Dakota,SD,0,0,0
47,Utah,UT,0,0,0
48,Virginia,VT,0,0,0
49,Wyoming,WY,0,0,0
50,Oklahoma,OK,0,0,0
51,Rhode Island,RI,0,0,0
My us-states.json is as in the following link https://raw.githubusercontent.com/alignedleft/d3-book/master/chapter_12/us-states.json
Code to add image and tooltip to each polygon.
paths.each(function(d) {
if (this.getTotalLength() > 0) {
var midPoint = path.centroid(d);
svg.append("svg:image")
.attr("height", "15px")
.attr("width", "15px")
.attr("xlink:href", "https://i.stack.imgur.com/O9xB5.png")
.attr("transform", "translate(" + midPoint[0] + ", " + midPoint[1] + ")")
.append("title")
.text(d.properties.abbr);
svg.append("svg:text")
.attr("x", midPoint[0])
.attr("y", midPoint[1])
.text(d.properties.abbr);
}
});
JSFiddle
To include abbr details to the data, code as shown below.
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.MembersPresent = dataValue;
json.features[j].properties.pop = +dataPop;
json.features[j].properties.abbr = data[i]["State Abbr"];
// Stop looking through the JSON
break;
}
}
Related
problem with D3 lasso circle element with text
I basically copied the example https://bl.ocks.org/skokenes/a85800be6d89c76c1ca98493ae777572 Then I got the code to work with my data. So, I can now get the lasso to work. But when I try to add back my old code for the circles to display a text-type tool tip, the lasso breaks. The code then puts the class variables such as not_possible or selected on the "text" elements rather than on the "circle" elements where they need to be. I found that is the issue by using Chrome developer tools. When the tool tips code is commented out, the lasso code works and the DOM looks like this: <circle cx="854" cy="37" fill="red" r="7" class="selected"></circle> When the tool tips code is live, the tool tips work but the lasso code doesn't work and the DOM looks like this: <circle cx="854" cy="37" fill="red" r="4.9"> <title r="3.5" class> ==$0 "curr = 89.7, prev = 89.5, geo = Alaska, measure = Percent Citizen, Born in the US" </title> </circle> I've tried changing the styles for the classes, for example, from ".possible" to "circle.possible" but that doesn't help. I've googled for suggestions but haven't found anything that I could make work. I've tried passing the circle selection thru lasso.items(circles) but that doesn't work. This is the lasso code that does work: the troublesome ".append title" and "text" lines are commented out. var margin = {top: 20, right: 15, bottom: 60, left: 60} , width = 960 - margin.left - margin.right , height = 960 - margin.top - margin.bottom; var xScale = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return d[1]; })]) .range([0, width]); var yScale = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return d[0]; })]) .range([height, 0]); var svgArea = d3.select('.content') .append('svg') .attr('width', width + margin.right + margin.left) .attr('height', height + margin.top + margin.bottom) .attr('class', 'chart'); var main = svgArea.append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') .attr('width', width) .attr('height', height) .attr('class', 'main'); main.append('g') .attr('transform', 'translate(0,' + height + ')') .attr('class', 'main axis date') .call(d3.axisBottom(xScale)); main.append('g') .append("text") .attr("x", width / 2) .attr("y", height + margin.bottom - 10) .style("text-anchor", "middle") .style("font", "14px times") .text("Current X"); main.append('g') .attr('transform', 'translate(0,0)') .attr('class', 'main axis date') .call(d3.axisLeft(yScale)); main.append('g') .append("text") .attr("transform", "rotate(-90)") .attr("x", 0 - (height / 2)) .attr("y", 0 - margin.left / 2) .style("text-anchor", "middle") .style("font", "14px times") .text("Previous Y"); var rScale = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return d[1]; })]) .range([ 4, 5 ]); var lasso_start = function() { lasso.items() .attr("r",7) .classed("not_possible",true) .classed("selected",false) ; }; var lasso_draw = function() { lasso.possibleItems() .classed("not_possible",false) .classed("possible",true) ; lasso.notPossibleItems() .classed("not_possible",true) .classed("possible",false) ; }; var lasso_end = function() { lasso.items() .classed("not_possible",false) .classed("possible",false) ; lasso.selectedItems() .classed("selected",true) .attr("r", 7) ; lasso.notSelectedItems() .attr("r", 3.5) ; }; var circles = main.selectAll("circle") .data(data) .enter().append("circle") .attr("cx", function (d,i) { return xScale(d[1]); } ) .attr("cy", function (d) { return yScale(d[0]); } ) .attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } }) .attr("r", function (d) { return rScale(d[1]); }) //.append("title") //.text(function(d) { // return "curr = " + d[1] + // ", prev = " + d[0] + // ", geo = " + d[2] + // ", measure = " + d[3]; // }) ; var lasso = d3.lasso() .items(circles) .closePathDistance(75) // max distance for the lasso loop to be closed .closePathSelect(true) // can items be selected by closing the path? .targetArea(svgArea) // area where the lasso can be started .on("start",lasso_start) // lasso start function .on("draw",lasso_draw) // lasso draw function .on("end",lasso_end); // lasso end function svgArea.call(lasso); Why does including ".title" and ".text" cause a problem? And how do I solve it? I don't think the problem is with the CSS, but here it is: <style> // styling for D3 chart .chart { background: #fdfefe; } .main text { font: 10px sans-serif; } // styling for D3-lasso .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } circle { fill-opacity: 0.4; } .dot { stroke: #000; } .lasso path { stroke: rgb(80,80,80); stroke-width: 2px; } .lasso .drawn { fill-opacity: 0.05 ; } .lasso .loop_close { fill: none; stroke-dasharray: 4,4; } .lasso .origin { fill: #3399FF; fill-opacity: 0.5; } .not_possible { fill: rgb(200,200,200); } .possible { fill: #EC888C; } .selected { fill: steelblue; } </style>
The problem appears to be that lasso is adding a radius attribute to the title elements here: lasso.notSelectedItems() .attr("r", 3.5) ; resulting in all your not-selected elements, i.e., circles, and titles, having the attribute assigned, as your example suggests: <title r="3.5" class> Rather than calling lasso's selected and notSelected to change the radius and css class of the desired items, use a filter on the items array itself: // Style the selected dots lasso.items().filter(function(d) {return d.selected===true}) .classed(...) .attr("r",7); // Reset the style of the not selected dots lasso.items().filter(function(d) {return d.selected===false}) .classed(...) .attr("r",3.5); You can get as specific as you want with the return value, i.e., omit any nodes (like title nodes) you don't want affected by the rules you apply to the selection.
The problem was that I couldn't get D3 lasso and my approach to tool tips to work together. I was appending a title element to each circle (point) on a scatter plot. This does NOT work: var circles = main.selectAll("circle") .data(data) .enter().append("circle") .attr("cx", function (d,i) { return xScale(d[1]); } ) .attr("cy", function (d) { return yScale(d[0]); } ) .attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } }) .attr("r", function (d) { return rScale(d[1]); }) .append("title") .text(function(d) { return "curr = " + d[1] + ", prev = " + d[0] + ", geo = " + d[2] + ", measure = " + d[3]; }) ; I found a coding example by Mikhail Shabrikov that solved the issue by avoiding .append("title") altogether. This works: A new CSS element: .tooltip { position: absolute; z-index: 10; visibility: hidden; background-color: lightblue; text-align: center; padding: 4px; border-radius: 4px; font-weight: bold; color: black; } A new DIV element: var tooltip = d3.select("body") .append("div") .attr('class', 'tooltip'); And mainly a modified circles element: var circles = main.selectAll("circle") .data(data) .enter().append("circle") .attr("cx", function (d,i) { return xScale(d[1]); } ) .attr("cy", function (d) { return yScale(d[0]); } ) .attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } }) .attr("r", 5) .on("mouseover", function(d) {return tooltip.style("visibility", "visible") .text( "curr = " + d[1] + ", prev = " + d[0] + ", geo = " + d[2] + ", measure = " + d[3] ) }) .on("mousemove", function() { return tooltip.style("top", (event.pageY - 30) + "px") .style("left", event.pageX + "px"); }) .on("mouseout", function() { return tooltip.style("visibility", "hidden"); }) ; Shabrikov's code is near the very bottom of this item: circles, tool tips, mouse events
Paths not drawn after dragging in parallel coordinate in d3 V4
Here is a simple parallel coordinate in d3 V4 http://plnkr.co/edit/Ejg7CI7STPqXKB2tot51?p=preview It is similar to https://bl.ocks.org/jasondavies/1341281 , which is in V3. Following are the steps to reproduce the issue: Step1. Brush some area (say 0.8 to 0.4) in column1.... Step2. Brush some area (say 0.7 to 0.4) in column3.... Step3. Now drag the axis column3 to the position of column2. (So basically axis ordering will get changed, from Column1, 2 , 3, 4 .. it will change to column1, 3 ,2, 4. Step4. Brush column3 (which is now next to column1) again. You will see no paths are being drawn. Any help would be appreciated. Thanks <!DOCTYPE html> <meta charset="utf-8"> <style> svg { font: 10px sans-serif; } .background path { fill: none; stroke: #ddd; stroke-opacity: .4; shape-rendering: crispEdges; } .foreground path { fill: none; stroke: steelblue; stroke-opacity: .7; } .brush .extent { fill-opacity: .3; stroke: #fff; shape-rendering: crispEdges; } .axis line, .axis path { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; cursor: move; } </style> <body> <script src="http://d3js.org/d3.v4.min.js"></script> <script> var margin = {top: 30, right: 10, bottom: 10, left: 10}, width = 600 - margin.left - margin.right, height = 200 - margin.top - margin.bottom; var x = d3.scalePoint().rangeRound([0, width]).padding(1), y = {}, dragging = {}; var line = d3.line(), //axis = d3.axisLeft(x), background, foreground, extents; var container = d3.select("body").append("div") .attr("class", "parcoords") .style("width", width + margin.left + margin.right + "px") .style("height", height + margin.top + margin.bottom + "px"); var svg = 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 + ")"); var quant_p = function(v){return (parseFloat(v) == v) || (v == "")}; d3.json("convertcsvSO.json", function(error, cars) { dimensions = d3.keys(cars[0]); x.domain(dimensions); dimensions.forEach(function(d) { var vals = cars.map(function(p) {return p[d];}); if (vals.every(quant_p)){ y[d] = d3.scaleLinear() .domain(d3.extent(cars, function(p) { return +p[d]; })) .range([height, 0]) } else{ vals.sort(); y[d] = d3.scalePoint() .domain(vals.filter(function(v, i) {return vals.indexOf(v) == i;})) .range([height, 0],1); } }) extents = dimensions.map(function(p) { return [0,0]; }); // Add grey background lines for context. background = svg.append("g") .attr("class", "background") .selectAll("path") .data(cars) .enter().append("path") .attr("d", path); // Add blue foreground lines for focus. foreground = svg.append("g") .attr("class", "foreground") .selectAll("path") .data(cars) .enter().append("path") .attr("d", path); // Add a group element for each dimension. var g = svg.selectAll(".dimension") .data(dimensions) .enter().append("g") .attr("class", "dimension") .attr("transform", function(d) { return "translate(" + x(d) + ")"; }) .call(d3.drag() .subject(function(d) { return {x: x(d)}; }) .on("start", function(d) { dragging[d] = x(d); background.attr("visibility", "hidden"); }) .on("drag", function(d) { dragging[d] = Math.min(width, Math.max(0, d3.event.x)); foreground.attr("d", path); dimensions.sort(function(a, b) { return position(a) - position(b); }); x.domain(dimensions); g.attr("transform", function(d) { return "translate(" + position(d) + ")"; }) }) .on("end", function(d) { delete dragging[d]; transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")"); transition(foreground).attr("d", path); background .attr("d", path) .transition() .delay(500) .duration(0) .attr("visibility", null); })); // Add an axis and title. var g = svg.selectAll(".dimension"); g.append("g") .attr("class", "axis") .each(function(d) { d3.select(this).call(d3.axisLeft(y[d]));}) //text does not show up because previous line breaks somehow .append("text") .attr("fill", "black") .style("text-anchor", "middle") .attr("y", -9) .text(function(d) { return d; }); // Add and store a brush for each axis. g.append("g") .attr("class", "brush") .each(function(d) { if(y[d].name == 'r'){ // console.log(this); d3.select(this).call(y[d].brush = d3.brushY().extent([[-8, 0], [8,height]]).on("start", brushstart).on("brush", brush_parallel_chart)); } }) .selectAll("rect") .attr("x", -8) .attr("width", 16); }); // closing function position(d) { var v = dragging[d]; return v == null ? x(d) : v; } function transition(g) { return g.transition().duration(500); } // Returns the path for a given data point. function path(d) { return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; })); } // brush start function function brushstart() { d3.event.sourceEvent.stopPropagation(); } // Handles a brush event, toggling the display of foreground lines. function brush_parallel_chart() { for(var i=0;i<dimensions.length;++i){ if(d3.event.target==y[dimensions[i]].brush) { extents[i]=d3.event.selection.map(y[dimensions[i]].invert,y[dimensions[i]]); } } foreground.style("display", function(d) { return dimensions.every(function(p, i) { if(extents[i][0]==0 && extents[i][0]==0) { return true; } return extents[i][1] <= d[p] && d[p] <= extents[i][0]; }) ? null : "none"; }); } </script>
In the drag callbacks, the dimensions are being sorted BUT the extents aren't. I've added a few lines that sorts extents array based on the new dimensions (by using origDimensions which is the original array) Here's a fork of your plunkr: http://plnkr.co/edit/DquAXNv2mbbok7ssNuoX?p=preview Relevant code: var origDimensions = dimensions.slice(0); And within the dragend callback: // one way of sorting the extents array based on dimensions var new_extents = []; for(var i=0;i<dimensions.length;++i){ new_extents.push(extents[origDimensions.indexOf(dimensions[i])]); } extents = new_extents; origDimensions = dimensions.slice(0); // setting origDimensions to the new array Hope this helps. (and btw seems like the brushstart is empty which leads to showing NO curves on brush reset - try resetting brush on any axis).
Tooltip always displaying the same value
I'm trying to create a map of coordinates from some data I got in a csv file. The converting of the X/Y axes works perfectly, the circles (or rather dots) get drawn but the mouseover tooltip always displays the last values (or rather the last values +1 which is in my array out of bounds even though the tooltip should be set with the current values of the array. Longitude and altitude are my two array names var svgContainer = d3.select("body").append("svg") .attr("width", 700) .attr("height", 250) .style("border", "1px solid black"); var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); for (i = 0; i < longitude.length; i++) { var circleSelection = svgContainer.append("circle") .attr("cx", longitude[i]) .attr("cy", altitude[i]) .attr("r", 2) .style("fill", "purple") .on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div .html("X: " + longitude[i] + " Y: " + altitude[i]) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); }); } and here's the css but I doubt the problem's to be found in here <style> div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; } Every clue is much appreciated
As a general rule: do not use loops for appending elements in a D3 code. Not only this is not the idiomatic D3 but, more importantly, things will break (as you're seeing right now). Before anything, here is an explanation of why all the values are the same: JavaScript closure inside loops – simple practical example Let's see this, hover over any circle: var data = ["foo", "bar", "baz"]; var svg = d3.select("svg"); for (var i = 0; i < data.length; i++) { svg.append("circle") .attr("cy", 75) .attr("cx", 50 + i * 100) .attr("r", 20) .attr("fill", "teal") .on("mouseover", function() { console.log(data[i - 1]) }) } <script src="https://d3js.org/d3.v4.min.js"></script> <svg></svg> Things get better using let: var data = ["foo", "bar", "baz"]; var svg = d3.select("svg"); for (let i = 0; i < data.length; i++) { svg.append("circle") .attr("cy", 75) .attr("cx", 50 + i * 100) .attr("r", 20) .attr("fill", "teal") .on("mouseover", function() { console.log(data[i]) }) } <script src="https://d3js.org/d3.v4.min.js"></script> <svg></svg> However, even if using let gives the correct result, it is not a good solution, because you are not binding any data. The best solution is: use a D3 "enter" selection, binding data to the elements: var data = ["foo", "bar", "baz"]; var svg = d3.select("svg"); svg.selectAll(null) .data(data) .enter() .append("circle") .attr("cy", 75) .attr("cx", function(d, i) { return 50 + i * 100 }) .attr("r", 20) .attr("fill", "teal") .on("mouseover", function(d) { console.log(d) }) <script src="https://d3js.org/d3.v4.min.js"></script> <svg></svg>
Error: <rect> attribute y: Expected length, "NaN"
I am trying to follow this example here for a D3 stacked chart. I've tested it locally and it works fine. I have adapted the code to match my csv dataset, but unfortunately I get issues with the calculation of y and height attributes: Error: attribute y: Expected length, "NaN". Error: attribute height: Expected length, "NaN". Here is my adapted source code: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Enterprise Elements Analysis - In/Out of Scope</title> <script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script> <style type="text/css"> svg { font: 10px sans-serif; shape-rendering: crispEdges; } .axis path, .axis line { fill: none; stroke: #000; } path.domain { stroke: none; } .y .tick line { stroke: #ddd; } </style> </head> <body> <script type="text/javascript"> // Our D3 code will go here var ratData = []; d3.csv("./etcounts.csv", function(d) { return { type: d.type, in_scope: +d.in_scope, out_scope: +d.out_scope }; }, function(error, rows) { data = rows; console.log(data); createVisualization(); }); function createVisualization() { // Setup svg using with margins var margin = {bottom: 75, left: 15, right: 85}; var w = 200 - margin.left - margin.right; var h = 175 - margin.bottom; // get length of Array var arrayLength = data.length; // length of dataset var x_axisLength = 100; // length of x-axis in our layout var y_axisLength = 100; // length of y-axis in our layout var svg = d3.select("body") .append("svg") .attr("width", w + margin.left + margin.right) .attr("height", h + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + ",10)"); // set up the properties for stack var stack = d3.stack() .keys(["In Scope", "Out Scope"]) .order(d3.stackOrderDescending) .offset(d3.stackOffsetNone); // transpose your data using stack var series = stack(data); // view the stack console.log(series); // setup the Y scale var yScale = d3.scaleLinear() .domain([0, d3.max(series, function(d) { return d3.max(d, function(d) { return d[1]; }); })]) .range([h, 0]); // Set some colors into an array var colors = ["#dfd6d6", "#d85f41"]; // choose colors // Create groups for each series, rect elements for each segment var groups = svg.selectAll("g.type") .data(series) .enter().append("g") .attr("class", "type") .style("fill", function(d, i) { return colors[i]; // color the rectangles }); // Create the rectangles var rect = groups.selectAll("rect") .data(function(d) { return d; }) .enter() .append("rect") .attr("x", function(d,i) { return i * (x_axisLength/arrayLength) + 30; // Set x coordinate of rectangle to index of data value (i) *25 }) .attr("y", function(d) { return yScale(d[1]); // set base of rectangle }) .attr("height", function(d) { return yScale(d[0]) - yScale(d[1]); // set height of rectangle }) .attr("width", (x_axisLength/arrayLength) - 1) // set width of rectangle .on("mouseover", function() { tooltip.style("display", null); // hide tooltip }) .on("mousemove", function(d) { var xPosition = d3.mouse(this)[0] - 15; var yPosition = d3.mouse(this)[1] - 25; tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); tooltip.select("text").text(d.data.city + ": " + (d[1] - d[0])); // populate tooltip }) .on("mouseout", function() { tooltip.style("display", "none"); }); // Draw legend var legend = svg.selectAll(".legend") .data(colors) .enter().append("g") .attr("class", "legend") .attr("transform", function(d, i) { return "translate(" + i * 50 + ", 110)"; }); legend.append("rect") .attr("x", w - 70) .attr("width", 18) .attr("height", 18) .style("fill", function(d, i) {return colors.slice().reverse()[i];}); legend.append("text") .attr("x", w - 49) .attr("y", 9) .attr("dy", ".35em") .style("text-anchor", "start") .text(function(d, i) { switch (i) { case 0: return "In"; case 1: return "Out"; } }); // Prep the tooltip bits, initial display is hidden var tooltip = svg.append("g") .attr("class", "tooltip") .style("display", "none"); tooltip.append("text") .attr("x", 15) .attr("dy", "1.2em") .style("text-anchor", "middle") .attr("font-size", "12px"); // Create y-axis svg.append("line") .attr("x1", 30) .attr("y1", 0) .attr("x2", 30) .attr("y2", 100) .attr("stroke-width", 2) .attr("stroke", "black"); // y-axis label svg.append("text") .attr("class", "y label") .attr("text-anchor", "middle") .text("Elements") .attr("transform", "translate(20, 50) rotate(-90)") .attr("font-size", "14px") .attr("font-family", "'Open Sans', sans-serif"); // Create x-axis svg.append("line") .attr("x1", 30) .attr("y1", 100) .attr("x2", 130) .attr("y2", 100) .attr("stroke-width", 2) .attr("stroke", "black"); } </script> </body> </html> My Dataset (etcounts.csv) is here: type,in_scope,out_scope ERKRS,1,1 KKBER,6,5 KOKRS,1,31 BUKRS,78,143 VKORG,23,13 BWKEY,51,6 EKORG,5,6 WERKS,51,65 LGORT,9,180 SPART,9,3 VTWEG,2,0 PERSA,47,73 Unfortunately my D3/JS skills are not quite up to par, but I would appreciate any help. Thanks - John
Instead of var stack = d3.stack() .keys(["In Scope", "Out Scope"]) <-- there is no key as such .order(d3.stackOrderDescending) .offset(d3.stackOffsetNone); it should have been: var stack = d3.stack() .keys(["in_scope", "out_scope"]) .order(d3.stackOrderDescending) .offset(d3.stackOffsetNone); Reason: there is no keys in your CSV "In Scope", "Out Scope" It should have been "in_scope", "out_scope" EDIT For tool tip : tooltip.select("text").text(d.data.city + ": " + (d[1] - d[0])); should have been tooltip.select("text").text(d.data.type + ": " + (d[1] - d[0])); Reason: There is no data.city in your CSV. working code here
D3 js tooltip issue for Choropleth Map
i am new to D3.js. I have been trying to add tooltip to the existing d3 chloropleth Map by Michelle Chandra. However i am unable to make any progress, the tooltip doesnt seem to appear. Where am i doing wrong? Any Help will be appreciated. http://bl.ocks.org/michellechandra/0b2ce4923dc9b5809922. Thanks <style type="text/css"> /* On mouse hover, lighten state color */ path:hover { fill-opacity: .7; } /* Style for Custom Tooltip */ div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: white; border: 0px; border-radius: 8px; pointer-events: none; } /* Legend Font Style */ body { font: 11px sans-serif; } /* Legend Position Style */ .legend { position:absolute; left:800px; top:350px; } </style> </head> <body> <script type="text/javascript"> /* This visualization was made possible by modifying code provided by: Scott Murray, Choropleth example from "Interactive Data Visualization for the Web" https://github.com/alignedleft/d3-book/blob/master/chapter_12/05_choropleth.html Malcolm Maclean, tooltips example tutorial http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html Mike Bostock, Pie Chart Legend http://bl.ocks.org/mbostock/3888852 */ //Width and height of map var w = 900; var h = 600; // D3 Projection var projection = d3.geo.albersUsa() .translate([w/2, h/2]) // translate to center of screen .scale([1000]); // scale things down so see entire US // Define path generator var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths .projection(projection); // tell path generator to use albersUsa projection // Define linear scale for output var color = d3.scale.linear() .range(["rgb(24,143,95)","rgb(51,188,196)","rgb(155,226,183)","rgb(217,91,67)"]); var legendText = ["Cities Lived", "States Lived", "States Visited", "States Not Visited Yet"]; //Create SVG element and append map to the SVG var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); // Append Div for tooltip to SVG var div = d3.select("body") .append("div") .attr("class", "tooltip") .style("opacity", 0); // Load in my states data! d3.csv("stateslived.csv", function(data) { color.domain([0,1,2,3]); // setting the range of the input data // Load GeoJSON data and merge with states data d3.json("us-states.json", function(json) { // Loop through each state data value in the .csv file for (var i = 0; i < data.length; i++) { // Grab State Name var dataState = data[i].state; // Grab data value var dataValue = data[i].visited; // Find the corresponding state inside the GeoJSON for (var j = 0; j < json.features.length; j++) { var jsonState = json.features[j].properties.name; if (dataState == jsonState) { // Copy the data value into the JSON json.features[j].properties.visited = dataValue; // Stop looking through the JSON break; } } } // Bind the data to the SVG and create one path per GeoJSON feature svg.selectAll("path") .data(json.features) .enter() .append("path") .attr("d", path) .style("stroke", "#fff") .style("stroke-width", "1") .style("fill", function(d) { // Get data value var value = d.properties.visited; if (value) { //If value exists… return color(value); } else { //If value is undefined… return "rgb(213,222,217)"; } }); // Map the cities I have lived in! d3.csv("cities-lived.csv", function(data) { svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", function(d) { return projection([d.lon, d.lat])[0]; }) .attr("cy", function(d) { return projection([d.lon, d.lat])[1]; }) .attr("r", function(d) { return Math.sqrt(d.years) * 4; }) .style("fill", "rgb(217,91,67)") .style("opacity", 0.85) // add browser tooltip of city name //.append("title") //.text(function(d) { // return d.place; //}); // Modification of custom tooltip code provided by Malcolm Maclean, "D3 Tips and Tricks" // http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html .on("mouseover", function(d) { d3.select(this).transition().duration(300).style("opacity", 1); div.transition().duration(200) .style("opacity", .9); div.text(d.properties.visited) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) // fade out tooltip on mouse out .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); }); }); // Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852 var legend = d3.select("body").append("svg") .attr("class", "legend") .attr("width", 140) .attr("height", 200) .selectAll("g") .data(color.domain().slice().reverse()) .enter() .append("g") .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); legend.append("rect") .attr("width", 18) .attr("height", 18) .style("fill", color); legend.append("text") .data(legendText) .attr("x", 24) .attr("y", 9) .attr("dy", ".35em") .text(function(d) { return d; }); }); }); /* This code generate paths without mapping to other data // Load JSON file and generate path for each state d3.json("us-states.json", function(json) { // file path, callback function called when data loaded svg.selectAll("path") // creates empty references to all the paths .data(json.features) // loop through our data (the states in the array) and bind to paths .enter() // create placeholder to reference the new elements .append("path") // add to the DOM! .attr("d", path) // generate paths for each state .style("fill", "steelblue"); // make the states blue! }); */ </script>
I'm not sure what this statement is supposed to do: d3.select(this).transition().duration(300).style("opacity", 1); It appears to select the window object. One would need to look at your json file to see if you're getting the right data into the div. Shameless plug. Take a look at foxToolTip.js. I think its easier and more flexible than d3tip. https://github.com/MichaelRFox/foxToolTip.js
Once div is a div, you have to use html, not text: div.transition().duration(200) .style("opacity", .9); div.html(d.properties.visited) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px");