I am trying to follow the Choropleth example by Scott Murray (shown in his text book)using a timezone geojson file. The problem is my map ain't getting rendered and the entire SVG is getting filled with green color. My code is below, and I would like to know if I'm doing anything wrong in rendering the map. I fill colors in the map using the values under each timezone.(shown in the picture below).
<script>
var width = 1000;
var height = 600;
var d = [];
var projection = d3.geo.mercator()
.translate([width/2, height/2])
.scale([500]);
var path = d3.geo.path().projection(projection);
var color = d3.scale.ordinal().range(colorbrewer.YlGn[9]);
var svg = d3.select("#viz").append("svg")
.attr("width",width)
.attr("height",height);
d3.json('scm-timezone.json', function(data){
var com = data.commits;
d.push(com);
color.domain([
d3.min(d[0]),
d3.max(d[0])
]);
d3.json("world.json", function(json){
for(var i = 0;i < data.tz.length;i++){
var dataTZ = data.tz[i];
var commitValue = parseInt(data.commits[i]);
for(var j = 0;j<json.features.length;j++){
var jsonTZ = json.features[j].properties.description;
if(dataTZ == jsonTZ) {
json.features[j].properties.value = commitValue;
}
}
}
svg.selectAll("path")
.data(json.features)
.enter()
.insert("path")
.attr("d",path)
.style("fill", function(d) {
var val = d.properties.value;
if(val){ return color(val); }
else { return "white"; }
});
});
});
</script>
And my world.json finally looks like this-
Related
I try to enlarge my map with geoProjection or geoMercator. The mapsvg is hidden befind a black block, and my map cannot be seen. My code is as follows:
fetch("./data/chinaMapDataAll.json")
.then(d1 => d1.json())
.then(d1 => {
var mapdata = [];
var mapsvg = d3.select("#mapsvg")
var mapwidth = mapsvg.attr("width")
var mapheight = mapsvg.attr("height")
for(let i=0;i<=d1.features.length-1;i++){
mapdata.push(d1.features[i].geometry.coordinates[0][0]);
}
console.log(mapdata);
var mapProjection=d3.geoProjection(function(x,y){
return [x,y];
});
var mapPath = d3.geoPath(mapProjection);
mapsvg.selectAll("path")
.data(d1.features)
.join("path")
.attr("d", function(d) { return mapPath(d) })
.attr("fill",function(d,i) { return scacolor[i]; })
Inspired by https://www.nytimes.com/interactive/2018/03/19/upshot/race-class-white-and-black-men.html
I am trying to create a animation that will have markers move from one point to multiple levels in another point in the y axis. Thanks to detailed pages by Amelia and Mike Bostock in both bl.ocks.org and stackoverflow. I have got so far to get the circles and animate it. But, I am not able to make each marker loop over by the pathlevel and do the transition
Pathlevel, here, indicates whether they are high, middle or low (1,2,3).
The entire code in using d3.v4 has been pasted below. what I am missing?
Thanks for your help.
<!DOCTYPE html>
<meta charset="utf-8">
<title>SANKEY Experiment</title>
<style>
</style>
<body>
<script type="text/javascript" src="d3.v4.js"></script>
<script>
//ref: very important for path animation: https://stackoverflow.com/questions/21226523/trace-path-with-dom-object/21228701#21228701
//ref: clustered force layout: https://bl.ocks.org/mbostock/7881887
//ref: data manipulation: http://learnjsdata.com/iterate_data.html
var margin = {top: 30, right: 10, bottom: 30, left: 20},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var series = [{x:5,y:10},{x:150,y:10}],
series2 = [{x:5,y:10},{x:50,y:15},{x:100,y:30},{x:150,y:30}],
series3 = [{x:5,y:10},{x:50,y:22},{x:100,y:50},{x:150,y:50}];
var data = [{"dim":"a","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
{"dim":"b","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
{"dim":"a","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
{"dim":"b","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
{"dim":"a","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48},
{"dim":"b","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48}]
var x = d3.scaleLinear()
.domain([5,150])
.range([0,width]);
var y = d3.scaleLinear()
.domain([10,50])
.range([0,height]);
var line = d3.line()
.curve(d3.curveCardinal)
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
var svg = d3.select("body").append("svg").attr("height",600).attr("width",600);
var chart = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");
var update = function(series,k){
chart.append("path")
.attr("class","route"+k)
.attr("fill","none")
.attr("stroke","blue")
.attr("d",line(series));
}
update(series,1);
update(series2,2);
update(series3,3);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//create transistions along the path //////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var colorScale = d3.scaleOrdinal()
.domain(['a','b'])
.range(['orange','darkblue']);
//Get path start point for placing marker
var path = d3.select('.route1')
var startPoint = path.attr("d").split(",")[1];
//path example["M12.885906040268456", "84.48979591836735C12.885906040268456", "84.48979591836735", "241.07382550335572", "84.48979591836735",
//"318.9261744966443", "84.48979591836735C396.7785234899329", "84.48979591836735", "480", "84.48979591836735", "480", "84.48979591836735"]
//selecting class route which represents the path. d represents the path that is held by the path object. in that we split by comma and take the first
console.log(startPoint);
var glider = function(data,p){//p for path level
var marker = chart.selectAll(".marker")
.data(data)
.enter().append('circle')
.attr("class","marker")
.attr("fill",function(d){ return colorScale(d.dim);})
//.attr("x",function(d,i){return x(d.x1)+i*10;})
//.attr("y",function(d,i){return y(d.y1)+i*10;})
.attr("r",5);
//.attr("width",10)
//.attr("height",10);
var simulation = d3.forceSimulation()
.force("x",d3.forceX().strength(0.05))
.force("y",d3.forceY().strength(0.01))
.force('charge', d3.forceManyBody().strength(20))
.force("collide",d3.forceCollide(function(d){return y(d.y1)+4;}))
.alphaTarget(.03)
.restart();
simulation.nodes(data)
.on('tick',ticked);
function ticked(){
marker
.attr("cx",function(d){ return d.x;})
.attr("cy",function(d){ return d.y;})
}//end of ticked
//marker.transition().duration(3000).delay(200)
// .attr("x",function(d,i){return x(d.x2)+i*10;});
function translateAlong(path) {
var l = path.getTotalLength();
return function (d) {
return function (t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";//Move marker
}
}
}//end of translateAlong
console.log(marker);
function transition(){
var path2 = d3.select('.route'+p);
marker.attr("transform", "translate(" + startPoint + ")").transition().duration(3000).delay(function(d,i) { return i * 100; })
.attrTween("transform",translateAlong(path2.node()));
//.attr("x",function(d,i){return x(d.x2)+i*10;});
}//end of transition
transition();
}
/*var check = d3.map(data, function(d){return d.pathlevel;}).keys(); //for getting unique values from a column
check.forEach(function(i){
datapoints = data.filter(function(d){return d.pathlevel==i});
console.log(i);
glider(datapoints,i);
});*/
data1 = data.filter(function(d){return d.pathlevel==1});
data2 = data.filter(function(d){return d.pathlevel==2});
data3 = data.filter(function(d){return d.pathlevel==3});
//glider(data1,1);
//glider(data2,2);
glider(data3,3);
//glider(data,2);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*function createPathTween(d, i, a) {
var path = this.parentNode.getElementsByTagName("path")[1];
//i.e., go from this <circle> -> parent <g> -> array of child <path> elements
//-> first (and only) element in that array
//console.log(path);
var l = path.getTotalLength();
return function(t) {
var p = path.getPointAtLength(t * l);
console.log(p);
return "translate(" + p.x + "," + p.y + ")";
};
}//end of tweenpath*/
</script>
</body>
I am trying to use custom animation in plottable.js when data updates.
Below is my code : -
<script type="text/javascript">
var xScale = new Plottable.Scales.Category();
var yScale = new Plottable.Scales.Linear().domain([0,30]);
var xAxis = new Plottable.Axes.Category(xScale, "bottom");
var yAxis = new Plottable.Axes.Numeric(yScale, "left");
var dataset;
var data;
function createChart() {
data = [];
for(var i=0; i<10; i++) {
data.push({x:"Data" + (i + 1),y:Math.abs(Math.random() * 10)});
}
dataset = new Plottable.Dataset(data);
makeChart();
}
function updateChart() {
data = [];
for(var i=0; i<10; i++) {
data.push({x:"Data" + (i + 1),y:Math.abs(Math.random() * 10)});
}
dataset.data(data);
}
function makeChart() {
var linePlot = new Plottable.Plots.Line()
.addDataset(dataset)
.x(function(d) { return d.x; }, xScale)
.y(function(d) { return d.y; }, yScale)
.attr("stroke","#FA8116")
.animated(true)
.animator("test",new Plottable.Animators.Easing().easingMode("bounce"));
var label_y = new Plottable.Components.AxisLabel("Parameter 2", -90);
var label_x = new Plottable.Components.AxisLabel("Parameter 1", 0);
var chart = new Plottable.Components.Table([
[label_y, yAxis, linePlot],
[null, null, xAxis],
[null, null, label_x]
]);
chart.renderTo("svg#lineChart");
// Responsive Layout
window.addEventListener("resize", function() {
chart.redraw();
});
}
$(document).ready(function() {
createChart();
setInterval(function(d) {
updateChart();
},5000);
});
</script>
I want to animate lineplot other than default and I did this :-
var linePlot = new Plottable.Plots.Line()
.addDataset(dataset)
.x(function(d) { return d.x; }, xScale)
.y(function(d) { return d.y; }, yScale)
.attr("stroke","#FA8116")
.animated(true)
.animator("test",new Plottable.Animators.Easing().easingMode("bounce"));
I don`t understand where I am in correct and since I am new to plottable can you guys help me out, also is there a way to use d3 based animation with plottable ?? If yes can you provide a code snippet
Thanx in advance
Plots normally have two Animators: MAIN and RESET. You need to specify that you want to change the primary Animator on the Plot:
plot.animator(Plottable.Plots.Animator.MAIN,
new Plottable.Animators.Easing().easingMode("bounce"));
Right now I recalculate all the points which I select on the heatmap. I am relatively new at D3 and don't particularly understand how the zoom behavior works. Can someone help? This is the chunk of code that allows me to zoom into the heatmap with self selected Area:
function selectArea(area,svg,dataset,num,oldxStart,oldyStart) {
svg
.attr("width",width)
.attr("height",height)
var cols = dataset.dim[1]; //x
var rows = dataset.dim[0]; //y
var zoomDat = [];
var newxLab=[];
var newyLab = [];
//Makes the selection rectangles
area
.on("mousedown", function() {
var e = this,
origin = d3.mouse(e),
rect = svg
.append("rect")
.attr("class", "zoom");
origin[0] = Math.max(0, Math.min(width, origin[0]));
origin[1] = Math.max(0, Math.min(height, origin[1]));
d3.select('body')
.on("mousemove.zoomRect", function() {
var m = d3.mouse(e);
m[0] = Math.max(0, Math.min(width, m[0]));
m[1] = Math.max(0, Math.min(height, m[1]));
rect.attr("x", Math.min(origin[0], m[0]))
.attr("y", Math.min(origin[1], m[1]))
.attr("width", Math.abs(m[0] - origin[0]))
.attr("height", Math.abs(m[1] - origin[1]));
})
.on("mouseup.zoomRect", function() {
var m = d3.mouse(e);
m[0] = Math.max(0, Math.min(width, m[0]));
m[1] = Math.max(0, Math.min(height, m[1]));
//x,y Start/Finish for the selection of data => Can draw box the other way, and still work.
var xStart = Math.min(Math.floor(origin[0]/xScale(1)), Math.floor(m[0]/xScale(1)))
var xFinish = Math.max(Math.floor(m[0]/xScale(1)), Math.floor(origin[0]/xScale(1)))+1
var yStart = Math.min(Math.floor(origin[1]/yScale(1)), Math.floor(m[1]/yScale(1)))
var yFinish =Math.max(Math.floor(m[1]/yScale(1)), Math.floor(origin[1]/yScale(1)))+1
var newcolMeta = [];
var newrowMeta = [];
var newyDend = [];
var newxDend = [];
//If the Y dendrogram is selected, make the X dendrogram undefined
//because I dont want the x dendrogram to change
if (num==1) {
xStart = 0;
xFinish = cols
//If the X dendrogram is selected, make the y dendrogram undefined
//because I dont want the y dendrogram to change
} else if (num==2) {
yStart = 0;
yFinish = rows
}
//Get the data selected and send it back to heatmapgrid
for (i = xStart; i<xFinish; i++) {
newxLab.push(dataset.cols[i]);
if (data.cols!=null) { //If there is no column clustering
newxDend.push(d3.select(".ends_X"+i).attr("id"))
}
}
for (i=yStart;i<yFinish; i++) {
newyLab.push(dataset.rows[i]);
if (data.rows !=null) { //If there is no row clustering
newyDend.push(d3.select(".ends_Y"+i).attr("id"))
}
for (j=xStart; j<xFinish; j++) {
zoomDat.push(dataset.data[i*cols+j]);
}
}
//Get the Metadata -> If there is more than one line of annotations, the data is in different places, just like the grid
if (colMeta !=null) {
for (i = 0; i<colHead.length; i++) {
for (j = xStart; j<xFinish; j++) {
newcolMeta.push(colMeta[i*cols+j])
}
}
colMeta = newcolMeta
}
if (rowMeta != null) {
for (i =0; i<rowHead.length; i++) {
for (j =yStart; j<yFinish; j++) {
newrowMeta.push(rowMeta[i*rows+j])
}
}
rowMeta = newrowMeta
}
//Set new parameters based on selected data
dataset.dim[1] = newxLab.length;
dataset.dim[0] = newyLab.length;
dataset.rows = newyLab;
dataset.cols = newxLab;
dataset.data = zoomDat;
colAnnote.data = colMeta;
rowAnnote.data = rowMeta;
//Changes the margin, if the dimensions are small enough
if (dataset.dim[0] <=100) {
marginleft=100;
}
if (dataset.dim[1] <=300) {
margintop = 130;
}
xGlobal.range([0,width-marginleft])
yGlobal.range([0,height-margintop])
var x = xGlobal(1);
var y = yGlobal(1);
//This slows down the program (Remove())
d3.selectAll('.rootDend').remove();
oldxStart += xStart
oldyStart += yStart
//olsxStart + xStart because the dendrogram is translated
heatmapGrid(el.select('svg.colormap'),dataset,oldxStart,oldyStart);
//New Vertical dendrogram
dendrogram(el.select('svg.rowDend'), data.rows, false, 250, height-margintop,newyDend,oldyStart,y);
//New Horizontal dendrogram
dendrogram(el.select('svg.colDend'), data.cols, true, width-marginleft, 250,newxDend,oldxStart,x);
//New annotation bar, if no annotations, don't do this
drawAnnotate(el.select('svg.colAnnote'), colAnnote,false, width-marginleft,10);
drawAnnotate(el.select('svg.rowAnnote'),rowAnnote,true,10,height-margintop);
//zoomDat = [];
//remove blue select rectangle
rect.remove();
});
});
The intent of this code is to click on a pie slice and have all of slices animated in a shrinking and expanding fashion. When I run the following code for a single element, they all work. When I try to animate all arcs at the same time, the smallest one disappears. Fiddle http://jsfiddle.net/fk03xyap/
var data = [50,20,30];
var svgConfig = {
height: 1000,
width: 1000,
id: 'mySvg',
transform: 'translate(500,300)'
};
var colors = ['blue','gray','green'];
var pie = d3.layout.pie().value(function(d) { return d;});
var arcGen = d3.svg.arc()
.outerRadius(100)
.innerRadius(10);
var svg = d3.select('body').append('svg').attr(svgConfig);
var g = svg.selectAll('.arc')
.data(pie(data))
.enter()
.append('g')
.attr('transform','translate(100,200)')
.attr('class','arc');
var paths = g.append('path').attr('d',arcGen).style('fill',function(d,i){
return colors[i];
});
d3.selectAll('path').on('click', function () {
var el = d3.select(this);
var g = d3.select('g');
var paths = d3.selectAll('path');
var endAngle,startAngle,orig;
paths.transition().duration(250).attrTween('d',function(d){
orig = d;
console.log('orig',orig);
endAngle = d.endAngle;
startAngle = d.startAngle;
console.log('first');
var interpolate = d3.interpolate(d.endAngle, d.startAngle +.01);
return function(t){
d.endAngle = interpolate(t);
return arcGen(d);
};
}).transition().duration(755).attrTween('d',function(r){
console.log('orig new',orig);
var interpolate = d3.interpolate(r.startAngle,endAngle);
return function(t){
r.endAngle = interpolate(t);
return arcGen(r);
};
});
});
You are overwriting the "original" end angle. I would stash it in the d element for later use. Cleaning this up, the code simplifies to:
d3.selectAll('path').on('click', function () {
paths.transition().duration(250).attrTween('d',function(d){
d.origEnd = d.endAngle;
var interpolate = d3.interpolate(d.endAngle, d.startAngle +.01);
return function(t){
d.endAngle = interpolate(t);
return arcGen(d);
};
}).transition().duration(755).attrTween('d',function(r){
var interpolate = d3.interpolate(r.startAngle,r.origEnd);
return function(t){
r.endAngle = interpolate(t);
return arcGen(r);
};
});
});
Updated example.