Im currently trying to wrap my head around connecting points around the map and animating them like - HERE:
http://www.tnoda.com/flightanimation
I want the points to connect using information in my CSV File:
Specifically 'where' field which will tell us is the place a destination or an origin of the flight:
code,city,country,lat,lon,where
ZNZ,ZANZIBAR,TANZANIA,-6.13,39.31,dest
TYO,TOKYO,JAPAN,35.68,139.76,dest
AKL,AUCKLAND,NEW ZEALAND,-36.85,174.78,orgin
BKK,BANGKOK,THAILAND,13.75,100.48,orgin
DEL,DELHI,INDIA,29.01,77.38,orgin
SIN,SINGAPORE,SINGAPOR,1.36,103.75,orgin
BSB,BRASILIA,BRAZIL,-15.67,-47.43,orgin
RIO,RIO DE JANEIRO,BRAZIL,-22.90,-43.24,orgin
YTO,TORONTO,CANADA,43.64,-79.40,orgin
IPC,EASTER ISLAND,CHILE,-27.11,-109.36,orgin
SEA,SEATTLE,USA,47.61,-122.33,orgin
(I know i spelled origin wrong its intentionally like that)
Now this is my HTML Code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
path {
stroke: limegreen;
stroke-width: 0.25px;
fill: black;
margin: 0 auto;
}
body {
background-color:darkgrey;
margin: 0 auto;
}
.packet {
max-height: height: 50px;
max-width: 50px;
fill: limegreen;
}
</style>
<body>
<script type="text/javascript" src="http://gc.kis.v2.scr.kaspersky-labs.com/3F7B1EB8-32BF-7449-968C-CB1318D27635/main.js" charset="UTF-8"></script><link rel="stylesheet" crossorigin="anonymous" href="http://gc.kis.v2.scr.kaspersky-labs.com/53672D8131BC-C869-9447-FB23-8BE1B7F3/abn/main.css"/><script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()// Creating our projection for our map
var svg = d3.select("body").append("svg")//Append svg to body
.attr("width", width)
.attr("height", height);
var path = d3.geo.path()//create a path for the projection
.projection(projection);
var g = svg.append("g"); //create an empty space to append
var packet = svg.append("path")
.attr("width","50px")
.attr("d", "M612.074,132.141v-2.38c0-8.849-4.016-19.26-11.229-26.473l-0.818-0.818c0,0-0.818,0-0.818-0.818 c-1.636-1.636-3.198-2.38-4.833-4.016c-0.818,0-0.818-0.818-1.636-0.818c-1.636-0.818-4.016-1.636-5.652-2.38 c-0.818,0-0.818-0.818-1.636-0.818c-2.38-0.818-4.833-1.636-7.213-1.636c-0.818,0-0.818,0-1.636,0c-2.38,0-5.651-0.818-8.849-0.818 H43.427c-3.198,0-6.395,0-9.667,0.818c-0.818,0-1.636,0-2.38,0.818c-2.38,0.818-4.834,0.818-6.395,1.636 c-0.818,0-0.818,0.818-1.636,0.818c-1.636,0.818-4.016,1.636-5.652,2.38l-0.818,0.818c-1.636,0.818-3.198,2.38-4.834,3.198 c-0.818,0.818-1.636,1.636-2.38,2.38C4.016,110.428,0.818,117.715,0,125.746c0,0.818,0,0.818,0,1.636v357.384 c0,0.818,0,0.818,0,1.636c1.636,11.229,7.213,20.896,15.244,26.473c7.213,4.833,16.062,8.031,26.473,8.031H569.39c0,0,0,0,0.818,0 l0,0c2.38,0,5.651,0,8.031-0.818c0.818,0,0.818,0,1.636,0c2.38-0.818,4.834-0.818,6.395-1.636h0.818 c17.698-6.395,24.911-21.714,24.911-36.14v-2.38v-0.818v-0.818V134.521c0-0.818,0-0.818,0-1.636 C612.074,132.959,612.074,132.959,612.074,132.141z M560.69,120.913l-252.98,246.51l-57.854-56.218l0,0L51.459,120.838H560.69 V120.913z M29.819,475.099V140.991l187.095,179.882L29.819,475.099z M299.679,491.905H56.292l182.336-149.393l58.597,57.036 c2.38,2.38,4.834,3.198,7.213,4.016h0.818c0.818,0,0.818,0,1.636,0l0,0c0.818,0,1.636,0,1.636,0h0.818 c2.38-0.818,5.651-1.636,7.213-4.016l55.4-53.838l183.079,146.196H299.679z M582.329,475.843L394.417,324.07L582.329,140.99 V475.843z");
var route = svg.append("path");
// load and display the World
d3.json("world-110m2.json", function(error, topology) {//Load in the world map ( LOW RES)
g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries)
.geometries)
.enter()
.append("path")//append path
.attr("d", path)//path is d3.geo.path which is the projection
//Loading the countries inside the world load display function to speed up the loading on local server and faster or client
//Loading the countries here also prevents the dots to be under the map instead on top of it!
d3.csv("countries.csv", function(error, data) {
g.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", 5)
.style("fill", "red");
//Writing out the Cities name
g.selectAll("text")
.attr("class","names")
.data(data)
.enter()
.append("text") // append text
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("dy", -7) // set y position of bottom of text
.style("fill", "limegreen") // fill the text with the colour black
.attr("text-anchor", "middle") // set anchor y justification
.text(function(d) {return d.city;}); // define the text to display
//Test
route.selectAll("path")
.datum({type: "LineString", coordinates:
[
function(d) {
if (d.where === origin){
return projection(d.lat,d.lon)
}},
function(d) {
if (d.where === dest){
return projection(d.lat,d.lon)
}}
]
})
.attr("class", "route")
.attr("d", path);
});
//Animating path
// Map Zooimng
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("path")
.attr("d", path.projection(projection));
});
svg.call(zoom)
});
function transition(packet, route) {
var l = route.node().getTotalLength();
packet.transition()
.duration(5000)
.attrTween("transform", delta(route.node()));
}
function delta(path) {
var l = path.getTotalLength();
return function(i) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
}
}
}
transition(packet, route);
</script>
</body>
</html>
Also if anyone can tell me why is my SVG element not resizing that would be great but i'd say i will be able to figure it out.
If someone can explain to me why is the code not working - point out my mistake in my logic and correct me it would be really helpful.
I'm doing this because Data Visualisation has conquered my interest now.
Cheers!
Also hope my comments can be helpful for people that might have had the same problem and are trying to wrap their head around the code!
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;
}
}
Edit: here is an example Fiddle: https://jsfiddle.net/3c9dtLyh/6/
I have a layout that I am attempting to animate to compare the arrangement on two different dates. What I would like to accomplish is a transition where items whose x,y position is different on the second date smoothly fly to the new position. I have attempted to do this using an updateData function set to trigger onclick.
The layout looks like this:
I do not neccesarily expect this approach to work because how would the transition know which (x,y) pairs correspond to the correct item name in the new arrangement. What am I missing about how these transitions work and how could I improve my approach?
Here is the code I am using. It's a relatively simple sequence of appending and svg element, drawing the rectangles, then (failing) to update their position on click.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
</style>
<body>
<div id = "chart">
</div>
<div id = "select_params">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
</body>
<!-- load js libraries -->
<script src="https://d3js.org/d3.v4.min.js"></script> <!-- uses v4 of d3 -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script> <!-- need to use this older version for tipsy -->
<script type="text/javascript" src="jquery.tipsy.js"></script> <!-- load from locally hosted source code -->
<!-- build the visualization -->
<script type='text/javascript'>
var item_width = 40, item_height = 60;
var margin = {top: 20, right: 50, bottom: 75, left: 40},
width = 700 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("#chart").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.csv("http://localhost:8080/udacity_test_vis_1/combined_item_pos.csv", function(data) {
// cast string to numeric
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
console.log(data);
var x_offset = 5, y_offset = 5;
x.domain(d3.extent(data, function(d) { return d.x_pos; })); // set the x domain
y.domain(d3.extent(data, function(d) { return d.y_pos; })); // set the y domain
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '1-20-2017'})
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("x", function(d) { return x(d.x_pos) + x_offset; }) // x position of dots
.attr("y", function(d) { return y(d.y_pos) + y_offset; }) // y position of dots
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
svg.selectAll("g")
.data(data)
.enter()
.append("text")
.filter(function(d){ return d.date == '1-20-2017'})
.attr("x", function(d) { return x(d.x_pos) + item_width/2 + x_offset; })
.attr("y", function(d) { return y(d.y_pos) + item_height/2 + y_offset; })
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.text(function(d){ return d.item_name});
});
function updateData() {
// grab the data again
d3.csv("http://localhost:8080/udacity_test_vis_1/combined_item_pos.csv", function(data) {
// cast string to numeric
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
var svg = d3.select("#chart").transition();
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '2-10-2017'})
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("x", function(d) { return x(d.x_pos) + x_offset; }) // x position of dots
.attr("y", function(d) { return y(d.y_pos) + y_offset; }) // y position of dots
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
svg.selectAll("g")
.data(data)
.enter()
.append("text")
.filter(function(d){ return d.date == '2-10-2017'})
.attr("x", function(d) { return x(d.x_pos) + item_width/2 + x_offset; })
.attr("y", function(d) { return y(d.y_pos) + item_height/2 + y_offset; })
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.text(function(d){ return d.item_name});
});
}
</script>
Here is my data:
,x_pos,y_pos,item_name,sales,date
0,1,1,S8221,2022,1-20-2017
1,2,1,NLC11,518,1-20-2017
2,3,1,35UUY,1614,1-20-2017
3,4,1,PPTNV,1059,1-20-2017
4,5,1,G0CWS,2183,1-20-2017
5,6,1,3JHUA,2513,1-20-2017
6,7,1,4HXGA,2251,1-20-2017
7,8,1,RYM9K,2330,1-20-2017
8,9,1,T8PUB,1476,1-20-2017
9,10,1,PLULW,1225,1-20-2017
10,1,2,YJ6S0,2403,1-20-2017
11,2,2,E9RGD,1361,1-20-2017
12,3,2,E2SW4,1131,1-20-2017
13,4,2,BZPGX,698,1-20-2017
14,5,2,0K682,1855,1-20-2017
15,6,2,D8UZW,2371,1-20-2017
16,7,2,USKY7,1851,1-20-2017
17,8,2,D0L0Y,1767,1-20-2017
18,9,2,P1AGP,1025,1-20-2017
19,10,2,9LT7O,1380,1-20-2017
20,1,3,1J184,1108,1-20-2017
21,2,3,RJDEG,2106,1-20-2017
22,3,3,LTSLR,1980,1-20-2017
23,4,3,ET3DF,2700,1-20-2017
24,5,3,42W1W,2194,1-20-2017
25,6,3,5QTJN,958,1-20-2017
26,7,3,O8XKY,2381,1-20-2017
27,8,3,LS9NW,516,1-20-2017
28,9,3,0MPZ7,2198,1-20-2017
29,10,3,R4E3J,2494,1-20-2017
30,1,4,WFPPY,2349,1-20-2017
31,2,4,MT2DB,2525,1-20-2017
32,3,4,6DRYS,600,1-20-2017
33,4,4,NVV0S,1556,1-20-2017
34,5,4,ODGZ2,912,1-20-2017
35,6,4,E3NLS,931,1-20-2017
36,7,4,9FFZ7,722,1-20-2017
37,8,4,UKZGF,2170,1-20-2017
38,9,4,XXORI,896,1-20-2017
39,10,4,QYU9Q,1104,1-20-2017
40,1,5,4KQPU,1562,1-20-2017
41,2,5,S3AYK,2298,1-20-2017
42,3,5,5W3CE,2580,1-20-2017
43,4,5,T0S7H,1677,1-20-2017
44,5,5,02SJG,1972,1-20-2017
45,6,5,GBMNZ,1845,1-20-2017
46,7,5,2Y7KH,982,1-20-2017
47,8,5,3WMOL,1952,1-20-2017
48,9,5,93KLU,2240,1-20-2017
49,10,5,K80OQ,2467,1-20-2017
50,1,6,2SIJS,1788,1-20-2017
51,2,6,5ZJ7V,2277,1-20-2017
52,3,6,HTL99,873,1-20-2017
53,4,6,C06QP,2185,1-20-2017
54,5,6,2S1YI,580,1-20-2017
55,6,6,IQ0L8,2395,1-20-2017
56,7,6,PEE2Y,2299,1-20-2017
57,8,6,6DEWK,2019,1-20-2017
58,9,6,9FY5B,1517,1-20-2017
59,10,6,NZQ54,2624,1-20-2017
60,1,7,C4SVV,1823,1-20-2017
61,2,7,Q4C4I,2339,1-20-2017
62,3,7,996OQ,1621,1-20-2017
63,4,7,PISK6,895,1-20-2017
64,5,7,KOKHE,1315,1-20-2017
65,6,7,6P4FT,1467,1-20-2017
66,7,7,3FY75,2085,1-20-2017
67,8,7,9YCNB,992,1-20-2017
68,9,7,NXXK1,2080,1-20-2017
69,10,7,4RDHV,2031,1-20-2017
0,6,1,9FFZ7,592,2-10-2017
1,1,6,E2SW4,622,2-10-2017
2,6,7,PLULW,1699,2-10-2017
3,8,3,ET3DF,784,2-10-2017
4,9,4,KOKHE,1092,2-10-2017
5,2,6,5ZJ7V,1691,2-10-2017
6,4,5,9FY5B,630,2-10-2017
7,9,4,G0CWS,1523,2-10-2017
8,9,2,PISK6,1778,2-10-2017
9,6,4,35UUY,2107,2-10-2017
10,3,5,5QTJN,1751,2-10-2017
11,6,6,NLC11,526,2-10-2017
12,8,2,C06QP,2308,2-10-2017
13,8,3,XXORI,1453,2-10-2017
14,5,1,E9RGD,1864,2-10-2017
15,7,2,HTL99,1222,2-10-2017
16,3,3,PEE2Y,2050,2-10-2017
17,9,7,GBMNZ,1941,2-10-2017
18,3,1,T8PUB,1440,2-10-2017
19,5,1,3WMOL,2692,2-10-2017
20,7,7,S3AYK,523,2-10-2017
21,1,5,BZPGX,2245,2-10-2017
22,2,1,S8221,2241,2-10-2017
23,9,7,IQ0L8,566,2-10-2017
24,8,5,D8UZW,1769,2-10-2017
25,3,1,RYM9K,1044,2-10-2017
26,4,6,4HXGA,2650,2-10-2017
27,2,2,WFPPY,2203,2-10-2017
28,2,4,93KLU,2289,2-10-2017
29,7,3,P1AGP,1084,2-10-2017
30,4,3,3JHUA,1364,2-10-2017
31,1,4,9LT7O,1198,2-10-2017
32,4,6,4RDHV,771,2-10-2017
33,10,7,T0S7H,873,2-10-2017
34,3,6,NXXK1,2391,2-10-2017
35,8,2,2SIJS,811,2-10-2017
36,8,4,LTSLR,1670,2-10-2017
37,6,7,02SJG,1880,2-10-2017
38,9,3,0MPZ7,2090,2-10-2017
39,2,6,E3NLS,2350,2-10-2017
40,7,6,QYU9Q,1092,2-10-2017
41,6,3,0K682,894,2-10-2017
42,1,5,LS9NW,1928,2-10-2017
43,7,7,NVV0S,951,2-10-2017
44,9,4,996OQ,670,2-10-2017
45,7,6,USKY7,706,2-10-2017
46,10,4,Q4C4I,2270,2-10-2017
47,4,2,UKZGF,1691,2-10-2017
48,10,3,RJDEG,597,2-10-2017
49,10,2,1J184,1921,2-10-2017
50,2,3,5W3CE,2604,2-10-2017
51,5,5,3FY75,1260,2-10-2017
52,1,1,6DEWK,2491,2-10-2017
53,7,5,9YCNB,1743,2-10-2017
54,4,7,6DRYS,2450,2-10-2017
55,5,2,MT2DB,1292,2-10-2017
56,8,5,C4SVV,1395,2-10-2017
57,3,7,ODGZ2,2685,2-10-2017
58,10,4,2S1YI,2617,2-10-2017
59,1,2,YJ6S0,1611,2-10-2017
60,6,3,2Y7KH,2188,2-10-2017
61,5,4,4KQPU,1413,2-10-2017
62,10,1,D0L0Y,2291,2-10-2017
63,5,1,NZQ54,1405,2-10-2017
64,5,2,6P4FT,1885,2-10-2017
65,3,1,PPTNV,1442,2-10-2017
66,1,5,K80OQ,2140,2-10-2017
67,4,5,42W1W,1697,2-10-2017
68,2,7,O8XKY,1007,2-10-2017
69,10,6,R4E3J,887,2-10-2017
So, I took a few minutes to completely refactor your code into proper d3 style. This aims to demonstrate a couple things:
The proper use of the enter, update, exit pattern.
Removed cut / paste duplicate code.
The proper way to use g to group elements and position them together.
How to add the transitions.
Here is the code running.
Commented code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
</style>
<body>
<div id="chart">
</div>
<div id="select_params">
<input name="updateButton" type="button" value="Update" onclick="updateData()" />
</div>
</body>
<!-- load js libraries -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- uses v4 of d3 -->
<!-- build the visualization -->
<script type='text/javascript'>
var item_width = 40,
item_height = 60;
var margin = {
top: 20,
right: 50,
bottom: 75,
left: 40
},
width = 700 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("#chart").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 + ")");
// a single function to draw
function draw(data, someDate) {
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
// pre-filter data
data = data.filter(function(d) {
return d.date === someDate
});
var x_offset = 5,
y_offset = 5;
x.domain(d3.extent(data, function(d) {
return d.x_pos;
})); // set the x domain
y.domain(d3.extent(data, function(d) {
return d.y_pos;
})); // set the y domain
// create an update selection with a key function
var g_sel = svg.selectAll("g")
.data(data, function(d) {
return d.item_name;
});
// get rid of those leaving the update
g_sel.exit().remove();
// our entering g
var g_ent = g_sel.enter()
.append("g");
// add our rects to our g
g_ent.append("rect")
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
// add our text to our g
g_ent.append("text")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("dx", item_width / 2)
.attr("dy", item_height / 2)
.text(function(d) {
return d.item_name
});
// UPDATE + ENTER selection
g_sel = g_ent.merge(g_sel);
// move them into position with transition
g_sel
.transition()
.attr("transform", function(d) {
return "translate(" + (x(d.x_pos) + x_offset) + "," + (y(d.y_pos) + y_offset) + ")";
});
}
d3.csv("test.csv", function(data) {
draw(data, '1-20-2017');
});
function updateData() {
d3.csv("test.csv", function(data) {
draw(data, '2-10-2017');
});
}
</script>
Heres my attempt: https://jsfiddle.net/guanzo/3c9dtLyh/10/
There are multiple data points that share the same position, which is why some rectangles are overlapping. I made a lot of changes to your code that resulted in less repetition.
Your data contains duplicate item_names with different dates/positions, but in your visualization you seem to want to only show items at a single date. Therefore, you only need to pass d3 data for a certain date, versus passing d3 ALL the data and then filtering.
Your code:
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '1-20-2017'})
My code:
var firstDateData = data.filter(d=>d.date == '1-20-2017');
var groups = svg.selectAll("g")
.data(firstDateData, d=> d.item_name)
The difference between these 2 is that in my example, D3 is only aware of a single set of item_names on date 1-20-2017. Therefore when i update the date with item_names on date 2-10-2017, D3 will automatically move all updated rectangles to their new position. How?
Here is where your question comes into play:
I do not neccesarily expect this approach to work because how would
the transition know which (x,y) pairs correspond to the correct item
name in the new arrangement
This is because i associated each rectangle with an item_name. D3s data function can take an optional 2nd parameter that specifies HOW the data is bound to the rectangles. This is called the key function.
svg.selectAll("g").data(firstDateData, d=> d.item_name)
In this case, i told d3 that each group (rectangles and their text) is bound to item_name. Therefore, the next time i pass data to D3, it tries to match existing elements (that are associated with an item_name) to the data (which contains item_names). If in my new data, i pass an item_name that corresponds to an existing element, and the data contains a new x and y position, D3 will move to element to that new position.
Note that even though i'm talking about rectangles, i bound data to the g element, which contains the rectangle and the text.
Feel free to ask any questions, i made a lot of changes that i didn't discuss.