Related
I have two questions.
Is it possible to still show the not selected data in corresponding scatter plot. Where there can be two scatters around the selected data points that the other data points stay or if there can be a color.
Is it possible to have multiple brushes in dc.js. Where I can select one part of data and do that again on another place in the same scatter plot.
For question 1
This is before the selection:
This after selection on graph. I would still like the not selected one to still appear:
What I would like for question 1
Here is my code sample:
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js - Scatter Plot Brushing Example</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../css/dc.css"/>
</head>
<body>
<div class="container">
<script type="text/javascript" src="header.js"></script>
<p>Brush on one chart to see the points filtered on the other.</p>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript" src="../js/d3.js"></script>
<script type="text/javascript" src="../js/crossfilter.js"></script>
<script type="text/javascript" src="../js/dc.js"></script>
<script type="text/javascript">
var chart1 = dc.scatterPlot("#test1");
var chart2 = dc.scatterPlot("#test2");
var data = "x,y,z\n" +
"1,1,3\n" +
"5,2,11\n" +
"13,13,13\n"+
"5,3,20\n"+
"12,12,10\n"+
"3,6,8\n"+
"15,2,9\n"+
"8,6,14\n"+
"1,4,9\n"+
"8,8,12\n";
var data = d3.csvParse(data);
data.forEach(function (x) {
x.x = +x.x;
x.y = +x.y;
x.z = +x.z;
});
var ndx = crossfilter(data),
dim1 = ndx.dimension(function (d) {
return [+d.x, +d.y];
}),
dim2 = ndx.dimension(function (d) {
return [+d.y, +d.z];
}),
group1 = dim1.group(),
group2 = dim2.group();
chart1.width(300)
.height(300)
.x(d3.scaleLinear().domain([0, 20]))
.yAxisLabel("y")
.xAxisLabel("x")
.clipPadding(10)
.dimension(dim1)
.excludedOpacity(0.5)
.group(group1);
chart2.width(300)
.height(300)
.x(d3.scaleLinear().domain([0, 20]))
.yAxisLabel("z")
.xAxisLabel("y")
.clipPadding(10)
.dimension(dim2)
.excludedColor('#ddd')
.group(group2);
dc.renderAll();
</script>
</div>
</body>
</html>
A1) That will be pretty difficult because dc.js sets the d of the paths of the not selected symbols in the other chart to d="M0,0". That means no path at all and all the symbols are now in the origin of the chart.
Edit
Looking at the code and after a little experiment I found if you add these then the other dots are visible
.emptySize(3)
.emptyOpacity(0.5)
The name is not very explanatory.
I have a pie chart for age, which currently has pie slices for every age there in the data set. Since the age range is wide, numerous thin slices are formed in the pie chart. I want to make it as a range, like one slice should show 0-18, another 19-30, and so on. How can I do this?
Here is my code
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="bower_components/dcjs/dc.css">
<link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css">
<script src="bower_components/d3/d3.min.js"></script>
<script src="bower_components/crossfilter2/crossfilter.min.js"></script>
<script src="bower_components/dcjs/dc.js"></script>
<!--THE FOLLOWING META IS IMPORTANT, OTHERWISE THERE MIGHT BE A PROBLEM WITH SOME CHARACTERS-->
<meta http-equiv="content-type" content="text/html; charset=UTF8">
<!--CDN FOR JQUERY-->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
</head>
<body>
<div id="map">
<a class="reset" href="javascript:usChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<span class="reset" style="display: none;"> | Current filter: <span class="filter"></span></span>
<div class="clearfix"></div>
</div>
<div id="pie-gender">
<a class="reset" href="javascript:usChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<span class="reset" style="display: none;"> | Current filter: <span class="filter"></span></span>
<div class="clearfix"></div>
</div>
<div id="pie-age">
<a class="reset" href="javascript:usChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<span class="reset" style="display: none;"> | Current filter: <span class="filter"></span></span>
<div class="clearfix"></div>
</div>
<div>
Reset All
</div>
<script type="text/javascript">
d3.csv("data/gender.csv", function (data) {
d3.json("data/us-states.json", function (json){
// set up crossfilter on the data.
var ndx = crossfilter(data);
// set up the dimensions
var stateDim = ndx.dimension(function (d) { return d.state; });
var genderDim = ndx.dimension(function(d) { return d.gender; });
var ageDim = ndx.dimension(function(d) { return d.age; });
//filtering age ranges
var age_0_18 = ageDim.filter([0,19]);
var age_19_30 = ageDim.filter([19,31]);
var age_31_60 = ageDim.filter([31,61]);
var age_61_101 = ageDim.filter([61,101]);
// set up the groups/values
var state = stateDim.group();
var gender = genderDim.group();
//var age = ageDim.group();
var age1 = age_0_18.group();
var age2 = age_19_30.group();
var age3 = age_31_60.group();
var age4 = age_61_101.group();
// the different charts - options are set below for each one.
var pieGender = dc.pieChart('#pie-gender');
var pieAge = dc.pieChart('#pie-age')
var usmap = dc.geoChoroplethChart("#map");
//create pie to show gender
pieGender
.width(180)
.height(180)
.radius(80)
.dimension(genderDim)
.group(gender)
.renderLabel(true)
.innerRadius(10)
.transitionDuration(500)
//.colorAccessor(function (d, i) { return d.value; });
//below is how to decide the colours for pie slices
.colors(d3.scale.ordinal().range([ '#14CAFF', '#4646FF']));
//creating pie to show age
pieAge
.width(180)
.height(180)
.radius(80)
.dimension(ageDim)
.group(age1,age2,age3,age4)
.renderLabel(true)
.innerRadius(10)
.transitionDuration(500)
.colorAccessor(function (d, i) { return d.value; });
//display US map
usmap
.width(900)
.height(500)
.dimension(stateDim)
.group(state)
.colors(["rgb(20,202,255)","rgb(70,70,255)"])
.overlayGeoJson(json.features, "name", function (d) { return d.properties.name; })
// at the end this needs to be called to actually go through and generate all the graphs on the page.
dc.renderAll();
});
});
</script>
</body>
I tried using filter and then grouping, but the result remained the same. I think the procedure is wrong maybe.
Any help would be greatly appreciated. Thanks.
I'm sure I've seen a lot of examples of this, but I couldn't find any in a quick search.
You'll want to use the group's groupValue function to put ages into the categories you want. This is exactly the same way you would round values down or do any other categorization:
var ageGroup = ageDim.group(function(v) {
if(v < 19) return "18 or under";
else if(v < 30) return "19-29";
else if(v < 30) return "30-59";
else return "over 60";
});
Note that dimension.filter() just returns the dimension and changes the filters for the entire crossfilter, so all of your groups above would be the same group, and only the last filter would take.
I have a time series with a date, an amount and a count column. I just want to plot the aggregate of amount by month and select a month by clicking on the bar, not using the brush.
I thought my objective was pretty simple, but I'm rummaging for days without success. The main issue is that I apply a filter on the chart, but the filter is not taken into account when a redraw the chart.
Thanks for your help.
I'm using :
dc.js 2.0.2
d3.js 3.5.17
crossfilter 1.4
This is my code :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Just selecting a month </title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../static/lib/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../static/lib/css/dc.css"/>
</head>
<body>
<div>
Month selector
<a class="reset" href='javascript:chart.filterAll();dc.redrawAll();'> Reset</a>
<div id="time-chart"></div>
</div>
<script type="text/javascript" src="../static/lib/js/d3.js"></script>
<script type="text/javascript" src="../static/lib/js/crossfilter.js"></script>
<script type="text/javascript" src="../static/lib/js/dc.js"></script>
<script type="text/javascript">
var dateFormat_in = d3.time.format.utc("%Y-%m-%d");
var chart = dc.barChart("#time-chart");
d3.csv('setdates.csv', function(error, dataset) {
if(error)
throw new Error(error);
dataset.forEach(function(d) {
d["date"] = dateFormat_in.parse(d["date"]);
d["amount"] = +d["amount"];
});
var ndx = crossfilter(dataset);
var monthDim = ndx.dimension(d => d3.time.month(d["date"]));
var monthGroup = monthDim.group().reduceSum(d => d["amount"]);
var minDate = monthDim.bottom(1)[0]["date"];
var maxDate = monthDim.top(1)[0]["date"];
minDate=d3.time.day.offset(minDate, -40);
//console.log([minDate,maxDate]);
chart
.width(400)
.height(260)
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.months)
.dimension(monthDim)
.group(monthGroup)
.margins({left: 50, top: 20, right: 0, bottom: 20})
.elasticY(true)
.gap(60)
.centerBar(true).xAxisPadding(15).xAxisPaddingUnit('month')
.on('pretransition', function(ichart) {
ichart.selectAll("rect.bar").on("click", function (d) {
console.log([d.data.key,new Date(2016,d.data.key.getMonth()+1,1)]);
chart.filter([d.data.key,new Date(2016,5,1)]).redraw();
console.log(chart.filters())
//dc.renderAll();
});
})
.brushOn(false)
.clipPadding(20);
chart.centerBar(true).xAxisPadding(15).xAxisPaddingUnit('month')
dc.renderAll();
});
</script>
These are my data :
date,amount,count
2016-04-28,93.54,3.89
2016-04-29,94.42,3.94
2016-04-30,95.30,3.99
2016-05-02,97.06,4.08
2016-05-03,98.50,4.11
2016-05-04,99.94,4.13
2016-05-06,102.82,4.18
2016-05-07,104.26,4.20
2016-05-09,107.14,4.25
2016-05-10,109.27,4.26
2016-05-11,111.40,4.26
2016-05-12,113.53,4.27
2016-05-13,115.66,4.27
2016-05-14,117.78,4.28
2016-05-17,124.17,4.30
2016-05-18,126.30,4.30
2016-05-19,128.43,4.31
2016-05-20,130.56,4.32
2016-05-21,132.68,4.32
2016-05-23,136.94,4.33
2016-05-24,139.14,4.40
2016-05-25,141.35,4.48
2016-05-26,143.55,4.55
2016-05-27,145.75,4.62
2016-05-28,147.96,4.69
2016-05-30,152.36,4.83
2016-05-31,153.70,4.88
2016-06-01,155.04,4.93
2016-06-02,156.38,4.98
2016-06-03,157.73,5.02
2016-06-04,159.07,5.07
2016-06-06,161.75,5.17
2016-06-07,161.22,5.15
2016-06-08,160.70,5.14
2016-06-09,160.17,5.13
2016-06-10,159.64,5.12
2016-06-11,159.11,5.11
2016-06-13,158.06,5.08
2016-06-14,156.32,5.06
2016-06-15,154.59,5.04
2016-06-16,152.85,5.01
2016-06-17,151.12,4.99
2016-06-18,149.38,4.96
Interesting solution to this problem.
You probably want .redrawGroup() instead of .redraw() inside that handler, and you'll also need to wrap your range inside a dc.js filter object, specifically RangedFilter: unlike crossfilter's dimension.filter() dc.js's chart.filter() takes an object not an array.
Initial Range selection in DC.js chart
http://jsfiddle.net/KFEAC/2/
I'd like to learn how to add a image from my hard drive into an HTML5 canvas. I don't wanna upload it, just load it from my hard drive dynamically from a browse window after a button is clicked.
I do believe this is possible without PHP.
Can anyone help?
HTML:
<input type="file" id="openimg"> <input type="button" id="load" value="Load" style="width:100px;"><br/>
Width and Height (px): <input type="text" id="width" style="width:100px;">, <input type="text" id="height" style="width:100px;"><br/>
<canvas id="myimg" width="300" height="300"></canvas>
JavaScript/JQuery:
$(function(){
$("canvas#myimg").draggable();
var canvas = document.getElementById("myimg");
var context = canvas.getContext("2d");
function draw() {
var chosenimg = $("#openimg").val();
var w = parseInt($("#width").val());
var h = parseInt($("#height").val());
canvas.width = w;
canvas.height = h;
var img = new Image();
img.onload = function () {
context.drawImage(img,0,0,img.width,img.height,0,0,w,h);
}
img.src = $("#openimg").val();}
$("#width").val(150);
$("#height").val(150);
$("#load").click(function(){ draw(); });
});
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas = document.getElementById("myimg");
var context = canvas.getContext("2d");
function draw() {
var chosenimg = $("#openimg").val();
var w = parseInt($("#width").val());
var h = parseInt($("#height").val());
canvas.width = w;
canvas.height = h;
var img = new Image();
img.onload = function () {
context.drawImage(img,0,0,img.width,img.height,0,0,w,h);
console.log(img.src);
}
img.src = $("#openimg").val();
}
$("#width").val(150);
$("#height").val(150);
$("#load").click(function(){ draw(); });
}); // end $(function(){});
</script>
</head>
<body>
<input type="file" id="openimg">
<input type="button" id="load" value="Load" style="width:100px;"><br/>
Width and Height (px):
<input type="text" id="width" style="width:100px;">,
<input type="text" id="height" style="width:100px;"><br/>
<canvas id="myimg" width="300" height="300"></canvas>
</body>
</html>
my json file looks like
{"items":[
{"date":"2012-03-12","scoreMin":"9","scoreMax":"25","scoreAverage":"20.39","scoreSTD":"3.86","scoreCount":"133","count20":"73","count25":"46"},
{"date":"2012-03-13","scoreMin":"9","scoreMax":"25","scoreAverage":"20.9","scoreSTD":"4.25","scoreCount":"99","count20":"56","count25":"46"},
{"date":"2012-03-14","scoreMin":"9","scoreMax":"25","scoreAverage":"20.9","scoreSTD":"4.25","scoreCount":"99","count20":"56","count25":"46"},
{"date":"2012-03-15","scoreMin":"9","scoreMax":"25","scoreAverage":"20.9","scoreSTD":"4.25","scoreCount":"99","count20":"56","count25":"46"},
{"date":"2012-09-15","scoreMin":"5","scoreMax":"24","scoreAverage":"18.55","scoreSTD":"5.65","scoreCount":"100","count20":"45","count25":"0"},
{"date":"2012-09-16","scoreMin":"5","scoreMax":"24","scoreAverage":"18.55","scoreSTD":"5.65","scoreCount":"100","count20":"45","count25":"0"},
{"date":"2012-09-17","scoreMin":"5","scoreMax":"24","scoreAverage":"18.59","scoreSTD":"5.67","scoreCount":"99","count20":"45","count25":"0"},
{"date":"2012-09-18","scoreMin":"5","scoreMax":"24","scoreAverage":"18.64","scoreSTD":"5.67","scoreCount":"100","count20":"46","count25":"0"}
]}
and my script is
<!DOCTYPE html>
<html>
<head>
<title>Date Axes</title>
<script language="javascript" type="text/javascript" src="jqplot/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="jqplot/jquery.jqplot.min.js"></script>
<script language="javascript" type="text/javascript" src="jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script>
<link rel="stylesheet" type="text/css" href="jqplot/jquery.jqplot.css" />
</head>
<body>
<h2>
Some Statistics</h2>
<div id="chartCanvas" style="height: 400px; width: 1000px; align">
</div>
<br />
<script type="text/javascript">
$(function(){
$(document).ready(function(){
alert('Document ready');
var objArrayData=[];
var objArray = [];
$.getJSON("data.json",function(data){
$.each(data.items, function(i,data){
objArrayData[i] =("['" + data.date + "'," + data.scoreAverage + "]");
});
alert( 'Fetched ' + objArrayData.length + ' items!');
console.log('object Data ' + objArrayData);
objArray = ("[" + objArrayData + "]");
console.log('object Array' + objArray);
var plot = $.jqplot('chartCanvas', [objArray], {
title:'Rubric Average Scores',
gridPadding:{right:35},
axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer,
tickOptions:{formatString:'%#m/%y'},
//tickOptions:{formatString:'%b-%y'},
min:'March 30, 2012',
tickInterval:'1 month',
angle: -30,
}},
yaxis:{label:'Average Score',
},
series:[{lineWidth:3, markerOptions:{style:'square'}}]
});
});
});
return false;
});
</script>
</body>
</html>
I am getting uncaught exception- No data error
Before plotting the chart, I am printing the array and there are all the values that I need.
What is missing? Where am I doing wrong?
Thanks for your help.
I don't understand why you are feeding data to the objArrayData array in the way you are doing here. The data doesn't become an array just because there are square brackets surrounding them (it will only appear pretty and satisfying and confusing in the cosole.log).
Use array.push() instead of the way you are doing it here.
And also make sure you make the data.scoreAverage a number before parsing it to the array.
You can do it using parseFloat() function.
So finally you can feed the data to the array like this.
objArrayData.push([data.date,parseFloat(data.scoreAverage)]);
Here's the modified working code.
<!DOCTYPE html>
<html>
<head>
<title>Date Axes</title>
<script language="javascript" type="text/javascript" src="../jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.jqplot.min.js"></script>
<script language="javascript" type="text/javascript" src="../plugins/jqplot.dateAxisRenderer.min.js"></script>
<link rel="stylesheet" type="text/css" href="../jquery.jqplot.css" />
</head>
<body>
<h2>
Some Statistics</h2>
<div id="chartCanvas" style="height: 400px; width: 1000px; align">
</div>
<br />
<script type="text/javascript">
$(function(){
$(document).ready(function(){
var objArrayData=[];
var objArray = [];
$.getJSON("data.json",function(data){
$.each(data.items, function(i,data){
objArrayData.push([data.date,parseFloat(data.scoreAverage)]);
});
console.log('object Data ' + objArrayData);
var plot = $.jqplot('chartCanvas', [objArrayData], {
title:'Rubric Average Scores',
gridPadding:{right:35},
axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer,
tickOptions:{formatString:'%Y-%m-%d'},
//tickOptions:{formatString:'%b-%y'},
min:'March 30, 2012',
tickInterval:'1 month',
angle: -30,
}},
yaxis:{label:'Average Score',
},
series:[{lineWidth:3, markerOptions:{style:'square'}}]
});
});
});
return false;
});
</script>
</body>
</html>
Hope this helps.
PS: Make sure you learn about arrays a little more.