I have an HTML page on which I am trying to display a sky plot. For the display, I am using the D3 library in JS.
Here is my code:
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
function createSkyplot(){
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
console.log(inview[elem].data[0]);
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push(d);
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
svg.selectAll("point").data(pos).enter().append("circle").attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"})
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
}
I use the updateSkyPlot(d) method to update the plot. The input for this method is a JSON object of the next type:
{"type" : "data", "inView" : {[prn, elv, azi], [prn, elv, azi] ..}}.
When I run this code in the HTML page I am able to produce the next plot:
My question is, what would be the best way to add the prn number inside the corresponding circles?
What should I add to the code to make it work?
Any help would be appreciated, thank you.
Here is Full HTML snippet:
<!DOCTYPE html>
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
var json = {"type" : "GSV",
"inView" : [{"data" : [1, 45, 90]},
{"data" : [3, 70, 225]}]
};
updateSkyPlot(json);
function createSkyplot(){
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
/////////////////////
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push(d);
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
svg.selectAll("point").data(pos).enter().append("circle").attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"})
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
}
</script>
</body>
</html>
Here is a possiblity:
<!DOCTYPE html>
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
var json = {"type" : "GSV",
"inView" : [{"data" : [1, 45, 90]},
{"data" : [3, 70, 225]}]
};
updateSkyPlot(json);
function createSkyplot(){
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
/////////////////////
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push({ "angle": d, "label": inview[elem].data[0] });
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a")
.attr("transform", function(d) {
var coors = line([d.angle]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
points.append("circle")
.attr("class", "point")
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
points.append("text")
.text( function(d) { return d.label })
.attr("transform", "translate(-4,5)")
}
</script>
</body>
</html>
I have kept the label in data (when you create the pos array with circle positions):
var pos = [];
for (var elem in inview) {
if (inview.hasOwnProperty(elem)) {
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
// This is the modified line:
pos.push({ "position": d, "label": inview[elem].data[0] });
// pos.push(d);
}
}
Then instead of directly creating the circles, I create an intermediate container which will contain for each data point both the circle and the text label; and which is translated to its position:
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a") // The container
.attr("transform", function(d) {
var coors = line([d.position]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
This way we can include for each data point both the circle:
points.append("circle")
.attr("class", "point")
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
and the label:
points.append("text")
.text( function(d) { return d.label })
.attr("transform", "translate(-4,5)")
Related
The data is not loading up with the newest version of d3 and its not working
Here's the link
var yMargin = 40;
var width = 800;
var height = 400;
var barWidth = width / 275;
var tooltip = d3.select(".visHolder").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
var overlay = d3.select(".visHolder").append("div")
.attr("class", "overlay")
.style("opacity", 0);
var svgContainer = d3.select(".visHolder")
.append("svg")
.attr("width", width + 100).attr("height", height + 60);
d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json', function(err, data) {
svgContainer.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", 80)
.text("Gross Domestic Product");
svgContainer.append("text")
.attr("x", width / 2 + 120)
.attr("y", height + 50)
.text('More Information: http://www.bea.gov/national/pdf/nipaguid.pdf')
.attr("class", "info");
var years = data.data.map(function(item) {
var quarter;
var temp = item[0].substring(5, 7);
if (temp === "01") {
quarter = "Q1";
} else if (temp === "04") {
quarter = "Q2";
} else if (temp === "07") {
quarter = "Q3";
} else if (temp === "10") {
quarter = "Q4";
}
return item[0].substring(0, 4) + " " + quarter
});
var yearsDate = data.data.map(function(item) {
return new Date(item[0]);
});
var xMax = new Date(d3.max(yearsDate));
xMax.setMonth(xMax.getMonth() + 3);
var xScale = d3.scaleTime()
.domain([d3.min(yearsDate), xMax])
.range([0, width]);
var xAxis = d3.axisBottom()
.scale(xScale);
var xAxisGroup = svgContainer.append("g")
.call(xAxis)
.attr("id", "x-axis")
.attr("transform", "translate(60, 400)");
var GDP = data.data.map(function(item) {
return item[1]
});
var scaledGDP = [];
var gdpMin = d3.min(GDP);
var gdpMax = d3.max(GDP);
var linearScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([0, height]);
scaledGDP = GDP.map(function(item) {
return linearScale(item);
});
var yAxisScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([height, 0]);
var yAxis = d3.axisLeft(yAxisScale)
var yAxisGroup = svgContainer.append("g")
.call(yAxis)
.attr("id", "y-axis")
.attr("transform", "translate(60,0)");
d3.select("svg").selectAll("rect")
.data(scaledGDP)
.enter()
.append("rect")
.attr("date-date", function(d, i) {
return data.data[i][0]
})
.attr("data-gdp", function(d, i) {
return data.data[i][1]
})
.attr("class", "bar")
.attr("x", function(d, i) {
return xScale(yearsDate[i]);
})
.attr("y", function(d, i) {
return height - d;
})
.attr("width", barWidth)
.attr("height", function(d) {
return d;
})
.style("fill", "#FD7272")
.attr("transform", "translate(60,0)")
.on("mouseover", function(d, i) {
overlay.transition()
.duration(0)
.style("height", d + "px")
.style("width", barWidth + "px")
.style("opacity", .9)
.style("left", (i * barWidth) + 0 + "px")
.style("top", height - d + "px")
.style("transform", "translateX(60px)");
tooltip.transition()
.duration(200)
.style("opacity", .9)
tooltip.html(years[i] + "<br>" + "$" +
GDP[i].toFixed(1).replace(/(\d)(?=(\d{3})+\.)/g, "$1,") + " Billion")
.attr("date-date", data.data[i][0])
.style("left", (i * barWidth) + 30 + "px")
.style("top", height - 100 + "px")
.style("transform", "translateX(60px)");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 0);
overlay.transition()
.duration(200)
.style("opacity", 0);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
<div class="main">
<div class="container">
<div id="title">United States GDP</div>
<div class="visHolder"></div>
</div>
</div>
In d3 v6., the old d3.json(<src>, <callback>) has been replaced in favour of d3.json(<src>).then(<successcallback>).catch(<errorcallback>);
The old freecodecamp example was written for an older version of d3.
That is why the following works, with minimal changes:
var yMargin = 40;
var width = 800;
var height = 400;
var barWidth = width / 275;
var tooltip = d3.select(".visHolder").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
var overlay = d3.select(".visHolder").append("div")
.attr("class", "overlay")
.style("opacity", 0);
var svgContainer = d3.select(".visHolder")
.append("svg")
.attr("width", width + 100).attr("height", height + 60);
d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json').then(function(data) {
svgContainer.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", 80)
.text("Gross Domestic Product");
svgContainer.append("text")
.attr("x", width / 2 + 120)
.attr("y", height + 50)
.text('More Information: http://www.bea.gov/national/pdf/nipaguid.pdf')
.attr("class", "info");
var years = data.data.map(function(item) {
var quarter;
var temp = item[0].substring(5, 7);
if (temp === "01") {
quarter = "Q1";
} else if (temp === "04") {
quarter = "Q2";
} else if (temp === "07") {
quarter = "Q3";
} else if (temp === "10") {
quarter = "Q4";
}
return item[0].substring(0, 4) + " " + quarter
});
var yearsDate = data.data.map(function(item) {
return new Date(item[0]);
});
var xMax = new Date(d3.max(yearsDate));
xMax.setMonth(xMax.getMonth() + 3);
var xScale = d3.scaleTime()
.domain([d3.min(yearsDate), xMax])
.range([0, width]);
var xAxis = d3.axisBottom()
.scale(xScale);
var xAxisGroup = svgContainer.append("g")
.call(xAxis)
.attr("id", "x-axis")
.attr("transform", "translate(60, 400)");
var GDP = data.data.map(function(item) {
return item[1];
});
var scaledGDP = [];
var gdpMin = d3.min(GDP);
var gdpMax = d3.max(GDP);
var linearScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([0, height]);
scaledGDP = GDP.map(function(item) {
return linearScale(item);
});
var yAxisScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([height, 0]);
var yAxis = d3.axisLeft(yAxisScale)
var yAxisGroup = svgContainer.append("g")
.call(yAxis)
.attr("id", "y-axis")
.attr("transform", "translate(60,0)");
d3.select("svg").selectAll("rect")
.data(scaledGDP)
.enter()
.append("rect")
.attr("date-date", function(d, i) {
return data.data[i][0]
})
.attr("data-gdp", function(d, i) {
return data.data[i][1]
})
.attr("class", "bar")
.attr("x", function(d, i) {
return xScale(yearsDate[i]);
})
.attr("y", function(d, i) {
return height - d;
})
.attr("width", barWidth)
.attr("height", function(d) {
return d;
})
.style("fill", "#FD7272")
.attr("transform", "translate(60,0)")
.on('click', function(event, d) {
console.log(d);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
<div class="main">
<div class="container">
<div id="title">United States GDP</div>
<div class="visHolder"></div>
</div>
</div>
To use click events, as per this migration guide, the new signature passes (event, data, index), not (data, index). The above snippet contains an example, where d is logged when a bar is clicked.
I have js code that plots a simple polar chart in the HTML page. I am trying to have it update the data on the plot every 1 second.
I was able to update the data and plot the new points, however, there are two things I have not figured out yet.
Right now every second I am cleaning the plot area and plotting everything new, instead I would like to update the data by creating a smooth sliding transition from the previous position to the new one.
The second issue is that the labeling of the points is not being deleted with the old point position, which creates multiple labeling on top of each other.
What should I add in order to create this smooth transition for the plotted points and their labels?
Here is my code snippet:
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var color = d3.scale.category20();
var deg2rad = Math.PI / 180;
var width = 400,
height = 350,
radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var gr = null;
createSkyplot();
updateSkyPlot();
function createSkyplot() {
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) {
return -r(d) - 4;
})
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) {
return d;
});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d - 90) + ")";
});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) {
return d < 360 && d > 90 ? "end" : null;
})
.attr("transform", function(d) {
return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null;
})
.text(function(d) {
return d + "°";
});
/////////////////////
}
function updateSkyPlot() {
var pos = [{
"position": [1, Math.random() * 20, Math.random() * 20],
"label": 1
}, {
"position": [3, Math.random() * 20, Math.random() * 20],
"label": 5
}];
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a") // The container
.attr("transform", function(d) {
var coors = line([d.position]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
points.append("circle")
.attr("class", "point")
.attr("r", 10)
.attr("fill", function(d, i) {
return color(i);
});
points.append("text")
.attr("class", "label")
.text(function(d) {
return d.label
})
.style("font-size", "10")
.attr("transform", "translate(-4,5)");
setTimeout(updateSkyPlot, 1000);
}
</script>
</body>
</html>
Code can also be seen here.
I have tried adding
svg.selectAll("label").remove(); or svg.selectAll('point.label').remove();
but it does not work and when i add svg.selectAll("text").remove(); it removes all the text in the plot which is obviously something i don't want.
Any help on fixing those issues would be appreciated. Thank you.
I have some problem in d3 v4 when I draw a chord. The question is 'Uncaught TypeError: g_outer.selectAll(...).data(...).enter is not a function' at the 53th in my code. But when I see the source code refering to https://bost.ocks.org/mike/uberdata/ and https://github.com/d3/d3/blob/master/CHANGES.md#chords-d3-chord. I have modified some errors, but it still can't work.
Here is my code:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var city_name = [ "English" , "America" , "German" , "Japan" , "Austrilia" ];
var population = [
[ 1000, 3045, 4567, 1234, 3714 ],
[ 3214, 2000, 2060, 124 , 3234 ],
[ 8761, 6545, 3000, 8045, 647 ],
[ 3211, 1067, 3214, 4000, 1006 ],
[ 2146, 1034, 6745, 4764, 5000 ],
];
var chord_layout = d3.chord(population)
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout.sortGroups();
var chords = chord_layout.sortChords();
var width = 600;
var height = 600;
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
//add color
g_outer.selectAll("path")
.data(groups)
.enter().append("path")
.style("fill", function(d) { return color20(d.index); })
.style("stroke", function(d) { return color20(d.index); })
.attr("d", outer_arc );
//add text
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each( function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
})
.attr("dy", ".35em") //width
.attr("transform", function(d){ //angle
return "rotate(" + (d.angle * 180 / Math.PI ) + ")" +
"translate(0,"+ -1.0*(outerRadius+10) +")" +
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d){
return d.name;
})
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path") //use the path as element
.attr("d", inner_chord) //
.style("fill", function(d) { return color20(d.source.index); })
.style("opacity" ,1)
.on("mouseover", function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout", function(d,i){
d3.select(this)
.transition()
.duration(1000)
.style("fill",color20(d.source.index));
});
</script>
</body>
</html>
I want to draw a chord like this:
But in google chrome, the chord can not appear. I am new to d3, can you help me? I will appreciate it if you give me a good idea! Thankyou!
You have a slew of mistakes in your code. The biggest this that you aren't understanding how d3.chord function works. The general flow in d3 is to set-up your layout function before giving it any data. In your case like this:
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout(population);
After fixing that, I blended this example with your code to produce:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var city_name = ["English", "America", "German", "Japan", "Austrilia"];
var population = [
[1000, 3045, 4567, 1234, 3714],
[3214, 2000, 2060, 124, 3234],
[8761, 6545, 3000, 8045, 647],
[3211, 1067, 3214, 4000, 1006],
[2146, 1034, 6745, 4764, 5000],
];
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var width = 600;
var height = 600;
var innerRadius = width / 2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var groups = chord_layout(population);
var g_outer = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(groups);
var group = g_outer.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
//add color
group.append("path")
.style("fill", function(d) {
return color20(d.index);
})
.style("stroke", function(d) {
return color20(d.index);
})
.attr("d", outer_arc);
//add text
group.append("text")
.attr("dy", ".35em") //width
.attr("transform", function(d,i) { //angle
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
return "rotate(" + (d.angle * 180 / Math.PI) + ")" +
"translate(0," + -1.0 * (outerRadius + 10) + ")" +
((d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d) {
return d.name;
});
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
g_outer.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", inner_chord)
.style("fill", function(d) {
return color20(d.source.index);
})
.style("stroke", "black")
.style("opacity", 0.6)
.on("mouseover", function(d, i) {
d3.select(this)
.style("fill", "yellow");
})
.on("mouseout", function(d, i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill", color20(d.source.index));
});
</script>
</body>
</html>
The code below displays marker-ends on arrows/paths/lines as intended, but the color of the marker-end does not vary by line (i.e., it is always the same orange color, not the color of its respective line). I think the code is defaulting to the color assigned to the first field of my data(?). Any advice would be appreciated.
<script src="http://www.protobi.com/javascripts/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="http://www.protobi.com/examples/pca/pca.js"></script>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 20, left: 20};
var width = 1500 - margin.left - margin.right;
var height = 1500 - margin.top - margin.bottom;
var angle = Math.PI * 0;
var color = d3.scale.category10();
var x = d3.scale.linear().range([width, 0]); // switch to match how R biplot shows it
var y = d3.scale.linear().range([height, 0]);
x.domain([-3.5,3.5]).nice()
y.domain([-3.5,3.5]).nice()
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
d3.csv("/brand.csv", function(error, data) {
var matrix = data.map(function(d){
return d3.values(d).slice(1,d.length).map(parseFloat);
});
var pca = new PCA();
matrix = pca.scale(matrix,true,true);
pc = pca.pca(matrix,2)
var A = pc[0]; // this is the U matrix from SVD
var B = pc[1]; // this is the dV matrix from SVD
var brand_names = Object.keys(data[0]); // first row of data file ["ATTRIBUTE", "BRAND A", "BRAND B", "BRAND C", ...]
brand_names.shift(); // drop the first column label, e.g. "ATTRIBUTE"
data.map(function(d,i){
label: d.ATTRIBUTE,
d.pc1 = A[i][0];
d.pc2 = A[i][1];
});
var label_offset = {
"Early/First line": 20,
"Unfamiliar":10,
"Convenient": -5
}
var brands = brand_names
.map(function(key, i) {
return {
brand: key,
pc1: B[i][0]*4,
pc2: B[i][1]*4
}
});
function rotate(x,y, dtheta) {
var r = Math.sqrt(x*x + y*y);
var theta = Math.atan(y/x);
if (x<0) theta += Math.PI;
return {
x: r * Math.cos(theta + dtheta),
y: r * Math.sin(theta + dtheta)
}
}
data.map(function(d) {
var xy = rotate(d.pc1, d.pc2, angle);
d.pc1 = xy.x;
d.pc2 = xy.y;
});
brands.map(function(d) {
var xy = rotate(d.pc1, d.pc2, angle);
d.pc1 = xy.x;
d.pc2 = xy.y;
});
var showAxis = false; // normally we don't want to see the axis in PCA, it's meaningless
if (showAxis) {
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("PC1");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("PC2");
}
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 10.5)
.attr("cx", function(d) { return x(d.pc1); })
.attr("cy", function(d) { return y(d.pc2); })
.style("fill", function(d) { return color(d['species']); })
.on('mouseover', onMouseOverAttribute)
.on('mouseleave', onMouseLeave);
svg.selectAll("text.brand")
.data(brands)
.enter().append("text")
.attr("class", "label-brand")
.attr("x", function(d) { return x(d.pc1) + 10; })
.attr("y", function(d) { return y(d.pc2) + 0; })
.text(function(d) { return d['brand']})
svg.selectAll("marker.brand")
.data(brands)
.enter().append("svg:marker")
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.style("fill", function(d) { return color(d['brand']); });
svg.selectAll(".line")
.data(brands)
.enter().append("line")
.attr("class", "square")
.attr('x1', function(d) { return x(-d.pc1);})
.attr('y1', function(d) { return y(-d.pc2); })
.attr("x2", function(d) { return x(d.pc1); })
.attr("y2", function(d) { return y(d.pc2); })
.style("stroke", function(d) { return color(d['brand']); })
.style('marker-end', "url(#end-arrow)")
.on('mouseover', onMouseOverBrand)
.on('mouseleave', onMouseLeave);
svg.selectAll("text.attr")
.data(data)
.enter().append("text")
.attr("class", "label-attr")
.attr("x", function(d,i ) { return x(d.pc1)+4 ; })
.attr("y", function(d ,i) { return y(d.pc2) + (label_offset[d.ATTRIBUTE]||0); })
.text(function(d,i) { return d.ATTRIBUTE})
var pctFmt = d3.format('0%')
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([10, 20])
.direction('e')
.html(function(values,title) {
var str =''
str += '<h3>' + (title.length==1 ? 'Brand ' : '' )+ title + '</h3>'
str += "<table>";
for (var i=0; i<values.length; i++) {
if (values[i].key != 'ATTRIBUTE' && values[i].key != 'pc1' && values[i].key != 'pc2') {
str += "<tr>";
str += "<td>" + values[i].key + "</td>";
str += "<td class=pct>" + pctFmt(values[i].value) + "</td>";
str + "</tr>";
}
}
str += "</table>";
return str;
});
svg.call(tip);
function getSpPoint(A,B,C){
var x1=A.x, y1=A.y, x2=B.x, y2=B.y, x3=C.x, y3=C.y;
var px = x2-x1, py = y2-y1, dAB = px*px + py*py;
var u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
var x = x1 + u * px, y = y1 + u * py;
return {x:x, y:y}; //this is D
}
// draw line from the attribute a perpendicular to each brand b
function onMouseOverAttribute(a,j) {
brands.forEach(function(b, idx) {
var A = { x: 0, y:0 };
var B = { x: b.pc1, y: b.pc2 };
var C = { x: a.pc1, y: a.pc2 };
b.D = getSpPoint(A,B,C);
});
svg.selectAll('.tracer')
.data(brands)
.enter()
.append('line')
.attr('class', 'tracer')
.attr('x1', function(b,i) { return x(a.pc1); return x1; })
.attr('y1', function(b,i) { return y(a.pc2); return y1; })
.attr('x2', function(b,i) { return x(b.D.x); return x2; })
.attr('y2', function(b,i) { return y(b.D.y); return y2; })
.style("stroke", function(d) { return "#aaa"});
delete a.D;
var tipText = d3.entries(a);
tip.show(tipText, a.ATTRIBUTE);
};
// draw line from the brand axis a perpendicular to each attribute b
function onMouseOverBrand(b,j) {
data.forEach(function(a, idx) {
var A = { x: 0, y:0 };
var B = { x: b.pc1, y: b.pc2 };
var C = { x: a.pc1, y: a.pc2 };
a.D = getSpPoint(A,B,C);
});
svg.selectAll('.tracer')
.data(data)
.enter()
.append('line')
.attr('class', 'tracer')
.attr('x1', function(a,i) { return x(a.D.x); })
.attr('y1', function(a,i) { return y(a.D.y); })
.attr('x2', function(a,i) { return x(a.pc1); })
.attr('y2', function(a,i) { return y(a.pc2); })
.style("stroke", function(d) { return "#aaa"});
var tipText = data.map(function(d) {
return {key: d.ATTRIBUTE, value: d[b['brand']] }
})
tip.show(tipText, b.brand);
};
function onMouseLeave(b,j) {
svg.selectAll('.tracer').remove()
tip.hide();
}
});
While you are creating an svg:marker for each line, you give them all the same id. When they are then used on your line elements, since they all have the same id you are only using one of them.
Simple fix, give them unique ids:
svg.selectAll("marker.brand")
.data(brands)
.enter().append("svg:marker")
.attr('id', function(d,i){
return 'end-arrow' + i; //<-- append index postion
})
...
svg.selectAll(".line")
.data(brands)
.enter().append("line")
.attr("class", "square")
.attr('x1', function(d) {
return x(-d.pc1);
})
.attr('y1', function(d) {
return y(-d.pc2);
})
.attr("x2", function(d) {
return x(d.pc1);
})
.attr("y2", function(d) {
return y(d.pc2);
})
.style("stroke", function(d) {
return color(d['brand']);
})
.style('marker-end', function(d,i){
return "url(#end-arrow"+i+")"; //<--use the one with the right id
})
....
Example here.
I want to display two charts in one page.The Pie chart gets displayed but the Grouped and Stacked bar chart is not displaying . I tried to change the Id name in but no luck :( . Will appreciate if anyone helps me with the code correction .
/* Display Matrix Chart ,Pie chart, Grouped and Stacked bar chart in one page */
<apex:page showHeader="false">
<apex:includeScript value="{!URLFOR($Resource.jquery1)}"/>
<apex:includeScript value="{!URLFOR($Resource.D3)}"/>
<apex:includeScript value="{!URLFOR($Resource.nvD3)}"/>
<div id="body" height="50%" width="100px" ></div> // Id for Pie chart
<div id="body1" height="30%" width="90px"></div> // Id for Stacked and Grouped bar chart
<script>
// Matrix Chart starts here
var drawChart = function(divId,matrixReportId) {
$.ajax('/services/data/v29.0/analytics/reports/'+matrixReportId,
{
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer {!$Api.Session_ID}');
},
success: function(response) {
console.log(response);
var chart = nv.models.multiBarChart();
var chartData = [];
document.getElementById(divId).innerHTML = '';
$.each(response.groupingsDown.groupings, function(di, de) {
var values = [];
chartData.push({"key":de.label, "values": values});
$.each(response.groupingsAcross.groupings, function(ai, ae) {
values.push({"x": ae.label, "y": response.factMap[de.key+"!"+ae.key].aggregates[0].value});
});
});
d3.select('#'+divId).datum(chartData).transition().duration(100).call(chart);
window.setTimeout(function(){
drawChart(divId,matrixReportId);
}, 5000);
}
}
);
};
$(document).ready(function(){
drawChart('chart','00O90000005SSHv');
});
// Pie Chart Starts here
var width =250 ,
height = 450,
radius = Math.min(width, height) / 2;
var data = [{"age":"<5","population":2704659},{"age":"14-17","population":2159981},
{"age":"14-17","population":2159981},{"age":"18-24","population":3853788},
{"age":"25-44","population":14106543},{"age":"45-64","population":8819342},
{"age":">65","population":612463}];
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
var svg = d3.select("#body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; });
// Grouped and Stacked Bar Chart starts here
var n = 2, // number of layers
m = 10, // number of samples per layer
stack = d3.layout.stack(),
layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
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: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
var svg = d3.select("#body").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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand());
}
// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {
function bump(a) {
var x = 1/(.1 + Math.random()),
y = 2*Math.random()-.5,
z = 10/(.1 + Math.random());
for (var i = 0; i < n; i++) {
var w = (i/n- y) * z;
a[i] += x * Math.exp(-w * w);
}
}
var a=[],i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
}
</script>
<svg id="chart" height="50%" width="500px" ></svg> // Id for Matrix chart
</apex:page>
Thanks in advance
first, as we discussed here (d3 Donut chart does not render), you should position your
<div id="body1" height="30%" width="90px"></div>
above the script.
second, you are missing the # in your second svg-declaration to correctly select the div by its id, it has to be
var svg = d3.select("#body1").append("svg")
you could also think about naming the second svg differently (eg svg2) so you don't override your first svg-variable (in case you want to do something with it later).