D3 Functions Not Working When Multiple Data Sources Are Used - javascript

I've adapted this "Making a world map with d3, topojson, and a csv" tutorial into a usable v5 version: https://www.youtube.com/watch?v=aNbgrqRuoiE&t=216s
However, the issue comes in at the point where I am to bring in a separate datasource, being the CSV file. When I bring it in and check the data, I get an array of objects, which is fine. But in order to turn the locations in the CSV into dots on the map using the long and lat coordinate values, I obviously need to access those column values within the CSV. In the tutorial, the author does that easily, using the following:
svg.selectAll("state-circle")
.data(data)
.enter().append("circle")
.attr("class", "state-circle")
.attr("r",2)
.attr("cx", function(d) {
var coords = projection([d.long, d.lat])
return coords[0];})
.attr("cy", function(d) {
var coords = projection([d.long, d.lat])
return coords[1];});
But given the version of D3 he's using, the way he's written the code makes that simple. I don't know how to do it using the v5 syntax adaptation.
So in order to do what he's done, I need to find a way to access the latitude and longitude attributes inside the objects. Now I'm pretty new to D3 but I managed to achieve this using the following calculation (but I'm open to a better method as I'm not sure this is technically the best way or that it will work with what he's done above):
var long = d3.entries({longitude: true});
var lat = d3.entries({latitude: true});
The problem is that this code only works when I test it in a separate .js file, where the CSV is the only data source. When I try to run the code inside the .js file that also contains the .json datasource, it doesn't work. It continues to return the original array of objects, with all its attributes.
This is all the code I'm using so far:
var margin = {top: 50, left: 50, right: 50, bottom: 50},
height = 400 - margin.top - margin.bottom,
width = 1600 - margin.left - margin.right;
var svg = d3.selectAll("body")
.append("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right)
.append("g");
d3.json("https://d3js.org/world-50m.v1.json").then(function (data)
{console.log(data);
var countries = topojson.feature(data,data.objects.countries).features;
// console.log(countries);
var projection = d3.geoMercator()
.translate([width/2, height/2])
.scale(100);
var path = d3.geoPath()
.projection(projection);
svg.selectAll(".country")
.data(countries)
.enter().append("path")
.attr("class", "country")
.attr("d", path)
.on("mouseover", function(d)
{d3.select(this).classed("hover", true)})
.on("mouseout", function(d)
{d3.select(this).classed("hover", false)});
});
d3.csv("TData.csv", function(d) {
return {
city: d.City,
continent: d.Continent,
country: d.Country,
dimension: d.Dimension,
identity: d.Identity,
score: +d.Score,
state: d.Subdivision,
trait: d.Trait,
index: d.Index,
longitude: d.Latitude,
latitude: d.Longitude
}
}).then(function(data) {
// console.log(data);
var long = d3.entries({longitude: true});
var lat = d3.entries({latitude: true});
console.log(long);
console.log(lat);
});
Sample of the CSV file, is:
City,Continent,Country,Dimension,Identity,Score,Subdivision,Trait,Index,Latitude,Longitude
Wilmington,North America,United States,Pride,1270858,45,Delaware,Ego,1,"39,7932","-75,6181"
Wilmington,North America,United States,Humility,1270858,23,Delaware,Selflessness,2,"39,7932","-75,6181"
Wilmington,North America,United States,Humility,1270858,23,Delaware,Generosity,3,"39,7932","-75,6181"
Wilmington,North America,United States,Anger,1270858,48,Delaware,Impatience,4,"39,7932","-75,6181"

With the given csv file, you had to parse the string coordinates to a float variable -- and for that you need to replace the commas there as well -- the following code works
var margin = {top: 50, left: 50, right: 50, bottom: 50},
height = 400 - margin.top - margin.bottom,
width = 1600 - margin.left - margin.right;
var svg = d3.selectAll("body")
.append("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right)
.append("g");
var gBackground = svg.append("g"); // appended first
var gDataPoints = svg.append("g"); // appended second
d3.json("https://d3js.org/world-50m.v1.json").then(function (data) {
console.log(data);
var countries = topojson.feature(data,data.objects.countries).features;
var long = d3.entries({longitude: true});
var lat = d3.entries({latitude: true});
console.log(long);
console.log(lat);
gBackground.selectAll(".country")
.data(countries)
.enter().append("path")
.attr("class", "country")
.attr("d", path)
.on("mouseover", function(d)
{d3.select(this).classed("hover", true)})
.on("mouseout", function(d)
{d3.select(this).classed("hover", false)});
});
var projection = d3.geoMercator();
// .translate([width/2, height/2])
// .scale(100);
// need fixing - not sure why they're not working
var path = d3.geoPath()
.projection(projection);
d3.csv("TruityData.csv", function(d) {
return {
city: d.City,
continent: d.Continent,
country: d.Country,
dimension: d.Dimension,
identity: d.Identity,
score: +d.Score,
state: d.Subdivision,
trait: d.Trait,
index: d.Index,
latitude: d.Latitude,
longitude: d.Longitude
}
}).then(function(data) {
// console.log(data[0]);
gDataPoints.selectAll("state-circle")
.data(data)
.enter().append("circle")
.attr("class", "state-circle")
.attr("r",3)
.attr("cx", function(d) {
var dln = parseFloat(d.longitude.replace(",", "."));
var dlt = parseFloat(d.latitude.replace(",", "."));
var coords = projection([dln, dlt]);
return coords[0];})
.attr("cy", function(d) {
var dln = parseFloat(d.longitude.replace(",", "."));
var dlt = parseFloat(d.latitude.replace(",", "."));
var coords = projection([dln, dlt]);
return coords[1];});
});

Related

Recreate D3 Multi-Line Chart on Resize (responsive)

In order to have a responsive D3 multi-line chart I have added a resize function but it doesn't seem to work although the function gets called:
var data = [{
Date: "2016-10-10",
ValueOne: 1,
ValueTwo: 0
}, {
Date: "2016-10-17",
ValueOne: 23,
ValueTwo: 2
}, {
Date: "2016-10-24",
ValueOne: 32,
ValueTwo: 17
}, {
Date: "2016-10-31",
ValueOne: 57,
ValueTwo: 40
}, {
Date: "2016-11-07",
ValueOne: 74,
ValueTwo: 56
}];
var margin = {top: 10, right: 50, bottom: 100, left: 50},
// Set default width and height
widther = (window.innerWidth),
width = widther - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Determine current size, which determines vars
function set_vars() {
var width = window.innerWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
}
function drawGraphic() {
var svg = d3.select('#charts')
.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 + ")");
//Parses date for correct time format
var formatTime = d3.timeFormat("%Y-%m-%d");
data.forEach(function(d) {
d.Date = new Date(d.Date)
});
var valueOneData = data.map(function(d) {
return {
date: d.Date,
value: d.ValueOne
}
});
var valueTwoData = data.map(function(d) {
return {
date: d.Date,
value: d.ValueTwo
}
});
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.Date
}));
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return d.ValueOne
}) * 1.05]);
var lineGenerator = d3.line()
.x(function(d) {
return xScale(d.date)
})
.y(function(d) {
return yScale(d.value)
});
var gX = svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale).tickFormat(function(d) {
return formatTime(d)
}).tickValues(data.map(function(d) {
return d.Date
})))
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "rotate(-65)")
.attr("y", 4)
.attr("x", -10)
.attr("dy", ".35em");
var gY = svg.append("g")
.call(d3.axisLeft(yScale));
var valueOneLine = svg.append("path")
.datum(valueOneData)
.attr("d", lineGenerator)
.style("fill", "none")
.style("stroke", "#124");
var valueTwoLine = svg.append("path")
.datum(valueTwoData)
.attr("d", lineGenerator)
.style("fill", "none")
.style("stroke", "#c7003b");
//RESPONSIVENESS ATTEMPT NO1
d3.select(window).on("resize", resized);
}
//Resize function
function resized() {
d3.select("svg").remove();
set_vars();
drawGraphic();
console.log("FUNCTION IS BEING CALLED")
}
set_vars();
drawGraphic();
//RESPONSIVENESS ATTEMPT NO2
window.addEventListener("resize", function(){ d3.select("svg").remove(); set_vars(); drawGraphic(); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="charts"></div>
In the snippet, I have tried two ways to do that. None of them make the chart recreate from scratch. The same issue applied in this jsfiddle.
The issue was with the parsing of the data every time the window was resized.
As the date within the data is parsed the first time, calling parseDate(d.date) will fail on every other call as it's already been parsed to a valid date. Do you get it?
Hence, moving the parsing code so that it's executed just once:
// parse data just once
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
Fiddle link: https://jsfiddle.net/a5rqt0L1/
Suggestion: I feel this isn't the right way to make a responsive chart i.e. removing SVG and re-appending to the body with all the configuration done multiple times. Here's how I'd do it:
Parse the data, append svg with initial height and width, append X, Y axes just once but move drawBars (to draw the actual bars) to a separate function that will use d3's own enter, exit and update selection logic.
On window resize, just change the SVG's height and width, re-render the axes by .call(xAxis)... and just call the drawBars function.
Hope this helps.

How to specify a data column as the property used in a line generator?

In d3.js it is possible to access parts of a dataset by using the syntax d.measure with d accessing the data property and "measure" accessing a specific field in our dataset. Based on the code I found on bl.ocks.org I created a line chart. I however wanted to alter the function dsLineChart() in such a way that I can pass the name of the column that I want to use for visualising the values on the y-axis, i.e. how to specify an argument dsLineChart(argument) that determines the column to use e.g. d.measure2 instead of d.measure.
See below for the script. The dataset I have contains the columns "measure", "measure2", "measure3" and "measure4" of which "measure" is visualised by d.measure, but I want to call e.g. dsLineChart("measure2") to use the same function but for another column.
e.g.
Dataset
var data = [
{group:"All",category:2011,measure:28107,measure2:53301,measure3:89015.40,measure4:138394},
{group:"All",category:2012,measure:39400,measure2:7001, measure3:55550.50,measure4:18004},
{group:"All",category:2013,measure:33894,measure2:690597,measure3:68289.50,measure4:17455},
{group:"All",category:2014,measure:55261,measure2:7172,measure3:73380.93,measure:418143} ];
Script
I have created a minimal working script that can be found on the following link Fiddle D3js line chart
Thanks to the of feedback #GerardoFurtado the resulting script is provided in below snippet and allows for calling the function dsLineChart() with different arguments resulting in linecharts using different measures e.g. dsLineChart("measure2") vs. dsLineChart("measure").
// dataset
var lineChartData = [{
category: 2011,
measure: 28107,
measure2: 53301,
measure3: 89015.40,
measure4: 138394
},
{
category: 2012,
measure: 39400,
measure2: 7001,
measure3: 55550.50,
measure4: 18004
},
{
category: 2013,
measure: 33894,
measure2: 690597,
measure3: 68289.50,
measure4: 17455
},
{
category: 2014,
measure: 55261,
measure2: 7172,
measure3: 73380.93,
measure: 418143
}
];
// layout
var margin = {
top: 20,
right: 10,
bottom: 0,
left: 50
},
width = 350 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// function to draw linechart
function dsLineChart(selMeasure) {
//convert object to array
var data = d3.values(lineChartData);
var property;
var measures = [selMeasure];
var xScale = d3.scaleLinear()
.domain([0, data.length - 1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d[selMeasure];
})])
.range([height, 0])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d[property]);
});
var svg = d3.select("#lineChart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("position", "absolute")
.attr("top", "10px")
.attr("left", "410px")
var plot = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("id", "lineChartPlot");
var paths = plot.selectAll(null)
.data(measures)
.enter()
.append("path")
.attr("class", "line")
.attr("d", function(d) {
property = d;
return line(data)
})
.attr("stroke", "lightgrey")
.attr("fill", "none")
.attr("stroke-width", "4px");
}
dsLineChart("measure2");
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineChart"></div>
The most "elegant" solution here, and probably the most idiomatic one, is nesting your data, in such a way that the y value property can have the same name for all lines.
However, this doesn't mean that what you're asking is not possible: it certainly is. You can specify what scale you pass to the line generator (for instance, have a look at this answer), and what property you use for each method.
For this to work, we'll first declare a variable:
var property;
That's the variable we'll use in the line generator:
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d[property]);
});
Now, let's get the real properties. Here I'm hardcoding them, but you can easily extract them from the data:
var measures = ["measure", "measure2", "measure3", "measure4"];
Then, we bind that array as data:
var paths = plot.selectAll(null)
.data(measures)
.enter()
.append("path")
Now comes the important part: in the callback, you simply assign the value of property, which is used by the line generator:
.attr("d", function(d) {
property = d;
return line(data)
})
All together, here is your code with those changes:
// dataset
var data = [{
group: "All",
category: 2011,
measure: 28107,
measure2: 53301,
measure3: 89015.40,
measure4: 138394
},
{
group: "All",
category: 2012,
measure: 39400,
measure2: 7001,
measure3: 55550.50,
measure4: 18004
},
{
group: "All",
category: 2013,
measure: 33894,
measure2: 690597,
measure3: 68289.50,
measure4: 17455
},
{
group: "All",
category: 2014,
measure: 55261,
measure2: 7172,
measure3: 73380.93,
measure: 418143
}
];
var property;
var measures = ["measure", "measure2", "measure3", "measure4"];
// layout
var margin = {
top: 20,
right: 10,
bottom: 0,
left: 50
},
width = 350 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// function to draw linechart
function dsLineChart() {
var firstDatasetLineChart = data
var xScale = d3.scaleLinear()
.domain([0, firstDatasetLineChart.length - 1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(firstDatasetLineChart, function(d) {
return d.measure;
})])
.range([height, 0])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d[property]);
});
var svg = d3.select("#lineChart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("position", "absolute")
.attr("top", "10px")
.attr("left", "410px")
var plot = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("id", "lineChartPlot");
var paths = plot.selectAll(null)
.data(measures)
.enter()
.append("path")
.attr("class", "line")
.attr("d", function(d) {
property = d;
return line(data)
})
.attr("stroke", "lightgrey")
.attr("fill", "none")
.attr("stroke-width", "4px");
}
dsLineChart();
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineChart"></div>

How to draw bubbles on a D3 Map with mercator projection?

I made a world map in D3 with a mercator projection and trying to draw circles/bubbles as well, but they are not showing up on the map. I am using the same projection for the paths for the map as for the circles to calculate the cx and cy projections, but get the following error in my code below:
Uncaught TypeError: Cannot read property 'coordinates' of null
var margin = {top: 20, right: 20, bottom: 20, left: 20};
var w = 1100 - margin.left - margin.right;
var h = 900 - margin.top - margin.bottom;
var svg = d3.select("#chart")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var meteorsData = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/meteorite-strike-data.json";
var geoData = "https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json";
var geo = {};
var meteors = {};
d3.json(geoData, function(data){
//load world map data
geo = data.features;
d3.json(meteorsData, function(data){
meteors = data.features;
var projection = d3.geo.mercator()
.scale(150)
.translate([w/2, h/2]);
var path = d3.geo.path()
.projection(projection);
svg.selectAll("path")
.data(geo)
.enter()
.append("path")
.attr("fill", "#95E1D3")
.attr("stroke", "#34495e")
.attr("stroke-width", 0.5)
.attr("class", "countries")
.attr("d", path);
svg.selectAll(".circles")
.data(meteors)
.enter()
.append("circle")
.attr("cx", function(d){ return projection(d.geometry.coordinates)[0]})
.attr("cy", function(d){ return projection(d.geometry.coordinates)[1]})
.attr("r", 10)
.attr("fill", "ccc");
})
})
Someone can help me? Thanks
So in the data you were using https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/meteorite-strike-data.json
there was one data point that did not have a geometry and that was causing the error
{
"type": "Feature",
"geometry": null,
"properties": {
"mass": "2250",
"name": "Bulls Run",
"reclong": null,
"geolocation_address": null,
"geolocation_zip": null,
"year": "1964-01-01T00:00:00.000",
"geolocation_state": null,
"fall": "Fell",
"id": "5163",
"recclass": "Iron?",
"reclat": null,
"geolocation_city": null,
"nametype": "Valid"
}
},
so if you put in a check in your cx and cy functions that should solve the issue:
.attr("cx", function(d){
if (d.geometry){
return projection(d.geometry.coordinates)[0];
}
})
.attr("cy", function(d){
if (d.geometry) {
return projection(d.geometry.coordinates)[1];
}
})
Working codepen here http://codepen.io/anon/pen/xgjrmR

D3 Stacked bar graph works in Tributary but nowhere else

This has the expected results I want but when I import the code into my HTML file as a script it doesn't show anything at all.
var PUBLIC = [50,40,10];
var NONPROFIT = [30,40,30];
var FOR_PROFIT = [70,15,15];
var data = [
{"key":"PUBLIC", "pop1":PUBLIC[0], "pop2":PUBLIC[1], "pop3":PUBLIC[2]},
{"key":"NONPROFIT", "pop1":NONPROFIT[0], "pop2":NONPROFIT[1], "pop3":NONPROFIT[2]},
{"key":"FORPROFIT", "pop1":FOR_PROFIT[0], "pop2":FOR_PROFIT[1], "pop3":FOR_PROFIT[2]}
];
var n = 3, // Number of layers
m = data.length, // Number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) { return d.key; }),
// Go through each layer (pop1, pop2 etc, that's the range(n) part)
// then go through each object in data and pull out that objects's population data
// and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d)
{
var a = [];
for (var i = 0; i < m; ++i)
{
a[i] = { x: i, y: data[i]['pop' + (d+1)] };
}
return a;
})),
// The largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
// The largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], .08);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
I believe I have all the required libraries imported and then some:
<!-- D3 Library -->
<script src='https://d3js.org/d3.v3.min.js' charset='utf-8'></script>
<!-- jQuery Mobile -->
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<!-- jQuery Main -->
<script src='https://code.jquery.com/jquery-2.2.3.min.js' charset='utf-8'></script>
While the code to get my variables are a bit simplified (i.e. plainly setting my arrays) they are the same format as what is put within the data array.
Furthermore, this example does not work within CodePen either when I import everything that Tributary uses for its base libraries. While, again, this isn't 100% of the code I have going into the creation a much simpler working example on Tributary does not work on CodePen.
D3 has done nothing but kick my butt these past few weeks and I'm in need of some guidance. Thanks.
You need to wait for the page to be fully loaded or your can put the code before the closing </body> tag.
Solution1:
$(function() {
//Put your code here;
});
Solution2:
<body>
<svg></svg>
<script>
//your code here
</script>
</body>
Demo: https://jsfiddle.net/iRbouh/ag6p4kkg/

d3 TypeError: Cannot read property 'length' of undefined

Hi I am using a large json file in d3, about 75 KB. It seems to work for the 32 data objects but then I get the error in the console Cannot read property 'length' of undefined. It seems like my json is ok since I put it in http://jsonlint.com/ and it validated. I know similar questions have been asked here but I'm new to d3 so don't know how to modify the code. I think it may have something to do with how d3 is getting the data from my json file.
Here is d3 code in its entirety:
function truncate(str, maxLength, suffix) {
if(str.length > maxLength) {
str = str.substring(0, maxLength + 1);
str = str.substring(0, Math.min(str.length, str.lastIndexOf(" ")));
str = str + suffix;
}
return str;
}
var margin = {top: 20, right: 200, bottom: 0, left: 20},
width = 300,
height = 650;
var start_year = 2004,
end_year = 2013;
var c = d3.scale.category20c();
var x = d3.scale.linear()
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var formatYears = d3.format("0000");
xAxis.tickFormat(formatYears);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("data.json", function(error, data) {
if (error) throw error;
x.domain([start_year, end_year]);
var xScale = d3.scale.linear()
.domain([start_year, end_year])
.range([0, width]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + 0 + ")")
.call(xAxis);
console.log(data.length);
var len = data.length;
for (var j = 0; j < len; j++) {
// try{
var g = svg.append("g").attr("class","journal");
var circles = g.selectAll("circle")
.data(data[j]['articles'])
.enter()
.append("circle");
var text = g.selectAll("text")
.data(data[j]['articles'])
.enter()
.append("text");
var rScale = d3.scale.linear()
.domain([0, d3.max(data[j]['articles'], function(d) { return d[1]; })])
.range([2, 9]);
circles
.attr("cx", function(d, i) { return xScale(d[0]); })
.attr("cy", j*20+20)
.attr("r", function(d) { return rScale(d[1]); })
.style("fill", function(d) { return c(j); });
text
.attr("y", j*20+25)
.attr("x",function(d, i) { return xScale(d[0])-5; })
.attr("class","value")
.text(function(d){ return d[1]; })
.style("fill", function(d) { return c(j); })
.style("display","none");
g.append("text")
.attr("y", j*20+25)
.attr("x",width+20)
.attr("class","label")
.text(truncate(data[j]['name'],30,"..."))
.style("fill", function(d) { return c(j); })
.on("mouseover", mouseover)
.on("mouseout", mouseout);
// }
// catch(err){
// console.log(err);
// continue;
// }
};
function mouseover(p) {
var g = d3.select(this).node().parentNode;
d3.select(g).selectAll("circle").style("display","none");
d3.select(g).selectAll("text.value").style("display","block");
}
function mouseout(p) {
var g = d3.select(this).node().parentNode;
d3.select(g).selectAll("circle").style("display","block");
d3.select(g).selectAll("text.value").style("display","none");
}
});
Sample json:
[{"articles":[[2004,25],[2005,25],[2006,26],[2007,31],[2008,20],[2009,26],[2010,19],[2011,18],[2012,24],[2013,17]],"total": 231,"name": " Acta Inf. " },
{"articles":[[2008,1]],"total": 1,"name": " nf. " },
{"articles":[[2005,27],[2006,30],[2007,27],[2008,75],[2009,31],[2010,34],[2011,46],[2012,35],[2013,60]],"total": 365,"name": " Displays " },
{"articles":[[2010,20],[2011,16],[2012,16]],"total": 52,"name": " IJKDB " },
{"articles":[[2004,61],[2005,70],[2006,72],[2007,71],[2008,79],[2009,65],[2010,80],[2011,77],[2012,82],[2013,121]],"total": 778,"name": " Computers in Industry " },
{"articles":[[2010,1]],"total": 1,"name": " rs in Industry " },
{"articles":[[2005,1]],"total": 1,"name": " ry " }, ...
EDIT: no longer getting the error in console, there was something wrong with my JSON..however still not displaying all entries in the visualization, here is my entire JSON file https://api.myjson.com/bins/425wh
EDIT 2: it all displays now! all the data was there but wasn't showing up because the height of the d3 canvas was too small
The problem is in the last entry. The last entry is an array which is in correct it should have be an object, so your dataset has array within array in the last record. You will need to flatten your data set array.
Add this after fetching the data from AJAX.
data = [].concat.apply([], data);//this will flatten all your array within array into a single array of records,
Edit
Second problem:
I only see a few that actually show up? for example the Journal with name "Artificial Intelligence in Education" does not appear.
The problem is that the svg height is less and the data to be displayed is more so it cuts off after 30 records.
So have a dynamic height like this:
d3.select("body svg").attr("height",len*20.2);//20.2 is approx height of one element
Now height will depend on the data length that needs to displayed.
I have updated the fiddle accordingly
Working code here.

Categories