I have two charts, the first, a line chart, in which the user can brush.
Based on the selection, a bar chart (only one bar) updates its value thanks to a specific function. I would like to apply this specific function in an efficient manner to the new array.
I started by following the complex reduce example. There is something wrong with my logic because the function std gets
the all dataset instead of an array. It seems that to put the function within the valueAccessor is not the right thing to do.
This is my code:
/**********************************
* Step0: javascript functions *
**********************************/
// instead of calculating the desired metric on every change, which is slow, we'll just maintain
// these arrays of rows and calculate the metrics when needed in the accessor
function groupArrayAdd(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.right(elements, keyfn(item));
elements.splice(pos, 0, item);
return elements;
};
}
function groupArrayRemove(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.left(elements, keyfn(item));
if(keyfn(elements[pos])===keyfn(item))
elements.splice(pos, 1);
return elements;
};
}
function groupArrayInit() {
return [];
}
/**********************************
* Step1: Load data from json file *
**********************************/
d3.json("{% url "block__time_series" portfolio value_date %}").then(function(data){
const dateFormatSpecifier = "%Y-%m-%d";
const dateFormat = d3.timeFormat(dateFormatSpecifier);
const dateFormatParser = d3.timeParse(dateFormatSpecifier);
const numberFormat = d3.format('.2f');
data.forEach(function(d) {
d.dd = dateFormatParser(d.date);
d.month = d3.timeMonth(d.dd); // pre-calculate month for better performance
d.returns = +d.returns;
d.value = +d.value;
});
/******************************************************
* Step2: Create the dc.js chart objects & ling to div *
******************************************************/
const myBarChart = new dc.BarChart('#my_bar_chart');
const myLineChart = dc.compositeChart('#my_line_chart');
const palette_color_block4 = ["#6c5373", "#8badd9", "#b6d6f2", "#45788c", "#6E87F2", "#996A4E",
"#BF7761", "#735360", "#D994B0", "#6C5373", "#7F805E", "#A6A27A", "#48BDCC", "#FFC956", "#f2f2f2"]
/************************************************
* Step3: Run the data through crossfilter *
************************************************/
var facts = crossfilter(data), // Gets our 'facts' into crossfilter
returns = function (d) {return +d.returns}
/*Here my function that I want to use */
function std(kv) {
return d3.deviation(kv.value, returns);
}
/******************************************************
* Step4: Create the Dimensions *
******************************************************/
const dateDimension = facts.dimension(d => d.dd);
var returnsDimension = facts.dimension(returns);
var volGroup = dateDimension.group().reduce(groupArrayAdd(returns), groupArrayRemove(returns),
groupArrayInit);
var valueGroup = dateDimension.group().reduceSum(function (d) {return d.value; });
const moveMonths = facts.dimension(d => d.month);
const monthlyMoveGroup = moveMonths.group().reduceSum(d => d.value);
/***************************************
* Step5: Create the Visualisations *
***************************************/
myBarChart /* dc.BarChart('#my_bar_chart', 'chartGroup')*/
.width(400)
.height(200)
.x(d3.scaleBand())
.xUnits(dc.units.ordinal)
.colorAccessor(d => d.key)
.ordinalColors(palette_color_block4)
.margins({left: 80, top: 30, right: 10, bottom: 20})
.elasticY(false)
.brushOn(false)
.controlsUseVisibility(false)
.valueAccessor(std)
.dimension(returnsDimension)
.group(volGroup);
mylineChart /*dc.compositeChart('#my_line_chart', 'chartGroup')*/
.width(800)
.height(200)
.transitionDuration(1000)
.margins({top: 20, right: 10, bottom: 10, left: 10})
.dimension(moveMonths)
.mouseZoomable(true)
.round(d3.timeMonth.round)
.xUnits(d3.timeMonths)
.renderHorizontalGridLines(true)
.legend(new dc.Legend().x(800).y(10).itemHeight(13).gap(5))
.brushOn(true)
.title( function(d) { return dateFormat(d.key) + ': ' + numberFormat(d.value);
})
.valueAccessor(function (d) { return d.value})
.compose([
dc.lineChart(mylineChart).group(valueGroup , data[0].name)
]);
/****************************
* Step6: Render the Charts *
****************************/
dc.renderAll();
});
Related
For the numberDisplay I only see examples in which all values are summed. But I would like to apply a filter so that I only see the values for a certain type.
In the example JSFIDDLE there is now a numberDisplay where all values are summed.
Is it also possible to only summarize the values of which for example the type is "cash"?
So in this case I would like to see the number 6 if nothing is selected.
Thank you in advance for your help.
HTML
<div id="demo1">
<h2>Markers with clustering, popups and single selection</h2>
<i>Renewable energy plants in Bulgaria in 2012</i>
<div class="lineChart"></div>
<div class="pie"></div>
<h5>I want here onlny the total number for type "cash" (nothing selected 6)</h5>
<div class="number_cash"></div>
</div>
<pre id="data">date type value
1/1/2020 cash 1
1/1/2020 tab 1
1/1/2020 visa 1
1/2/2020 cash 2
1/2/2020 tab 2
1/2/2020 visa 2
1/3/2020 cash 3
1/3/2020 tab 3
1/3/2020 visa 3
</pre>
JavaScript
function drawMarkerSelect(data) {
var xf = crossfilter(data);
const dateFormatSpecifier = '%m/%d/%Y';
const dateFormat = d3.timeFormat(dateFormatSpecifier);
const dateFormatParser = d3.timeParse(dateFormatSpecifier);
data.forEach(function(d) {
var tempDate = new Date(d.date);
d.date = tempDate;
})
var groupname = "marker-select";
var facilities = xf.dimension(function(d) {
return d.date;
});
var facilitiesGroup = facilities.group().reduceSum(d => +d.value);
var typeDimension = xf.dimension(function(d) {
return d.type;
});
var typeGroup = typeDimension.group().reduceSum(d => +d.value);
var dateDimension = xf.dimension(function(d) {
return d.date;
});
var dateGroup = dateDimension.group().reduceSum(d => +d.value);
var minDate = dateDimension.bottom(1)[0].date;
var maxDate = dateDimension.top(1)[0].date;
//numberDisplay cash
var all = xf.groupAll();
var ndGroup = all.reduceSum(function(d) {
return d.value; //6
});
//numbers
var number_cash = dc.numberDisplay("#demo1 .number_cash", groupname)
.group(ndGroup)
.valueAccessor(function(d) {
return d;
});
var pie = dc.pieChart("#demo1 .pie", groupname)
.dimension(typeDimension)
.group(typeGroup)
.width(200)
.height(200)
.renderLabel(true)
.renderTitle(true)
.ordering(function(p) {
return -p.value;
});
var lineChart = dc.lineChart("#demo1 .lineChart", groupname)
.width(450)
.height(200)
.margins({
top: 10,
bottom: 30,
right: 10,
left: 70
})
.dimension(dateDimension)
.group(dateGroup, "total spend")
.yAxisLabel("Transaction spend")
.renderHorizontalGridLines(true)
.renderArea(true)
.legend(dc.legend().x(1200).y(5).itemHeight(12).gap(5))
.x(d3.scaleTime().domain([minDate, maxDate]));
lineChart.yAxis().ticks(5);
lineChart.xAxis().ticks(4);
dc.renderAll(groupname);
}
const data = d3.tsvParse(d3.select('pre#data').text());
drawMarkerSelect(data);
CSS
pre#data {
display: none;
}
.marker-cluster-indiv {
background-color: #9ecae1;
}
.marker-cluster-indiv div {
background-color: #6baed6;
}
Crossfilter is a framework for mapping and reducing in JavaScript. The dimension and group key functions determine the mapping to group bins, and the group reduce functions determine how to add or remove a row of data to/from a bin.
When you use a groupAll, there is just one bin.
With that in mind, you can count cash rows at face value, and count other rows as zero, by writing your reduceSum function as
var ndGroup = all.reduceSum(function(d) {
return d.type === 'cash' ? d.value : 0;
});
Fork of your fiddle.
What is the right way to filter multi line chart using a time series chart as filter?
I need a time series chart for my focus chart that it is shown the image below. Whenever I brush on time series chart my focus chart needs to be filtered with respect to time series chart.
The time series chart needs to contain only X axis and time as its dimension and it should be interactive with focus chart with respect to time.
var totalNumber = null;
// ------ main chart function -------
function makeCompetitiveGraphs(error, keywords_data) {
errorHandle(error);
cleanedData = getCompositeChartData(keywords_data);
console.log("===", cleanedData);
minDate = moment.min(cleanedData.timeStamp);
maxDate = moment.max(cleanedData.timeStamp);
margins = { top: 27, right: 27, bottom: 36, left: 54 };
// create composite chart.
var composite = dc.compositeChart('#competitiveChart');
// create cross filter
var cf = crossfilter(cleanedData.keywordData);
// create dimensions.
var keywordDateDimension = cf.dimension(function (dp) { return dp.date;
});
var Group = keywordDateDimension.group();
// compose for key words
composeCharts = composeKeywords(dc, composite, keywordDateDimension);
// create chart.
composite
.width(width())
.height(height())
.transitionDuration(1000)
.x(d3.time.scale().domain([minDate, maxDate]))
.ordering(function (d) { return d.value; })
.elasticY(true)
.elasticX(true)
.margins(margins)
.legend(
dc.legend()
.x(1100)
.y(10)
.itemHeight(16)
.gap(8)
.horizontal(false)
)
.renderHorizontalGridLines(true)
.brushOn(false);
// compose the chart array.
composite.compose(composeCharts);
// render the chart
composite.render();
function getCompositeChartData(keywords) {
let momentTimeStamps = [];
let totalKeywordPerDay = [];
let allKeywords = [];
// clean data for d3js chart's
keywords.forEach((kob) => {
kob.sd.forEach((ob) => {
allKeywords.push({
name: kob.kn,
total: ob.value.total__,
date: new Date(moment(ob._id.mention_created_date_, "MMM-DD-YYYY-hh")._d),
});
momentTimeStamps.push(moment(moment(ob._id.mention_created_date_, "MMM-DD-
YYYY-hh")._d));
totalKeywordPerDay.push(ob.value.total__);
});
});
console.log("--------", allKeywords)
// apply date filter.
allKeywords = limiteDataToDateFilter(allKeywords);
return { "keywordData": allKeywords, "timeStamp": momentTimeStamps,
"totalKeywordPerDay": totalKeywordPerDay };
}
function limiteDataToDateFilter(allKeywords) {
cleanedDateWithDates = [];
allKeywords.forEach(element => {
if (moment(element.date).isAfter(moment().date(1).month(6)) &&
moment(element.date).isBefore(moment().date(30).month(8))) {
cleanedDateWithDates.push(element);
}
});
return cleanedDateWithDates;
}
function getReduce(keyword, keywordDateDimension) {
return keywordDateDimension.group().reduceSum(function (dp) {
return dp.name === keyword ? dp.total : 0;
});
}
function composeKeywords(dc, composite, keywordDateDimension) {
composeChartsData = []
keywordsParams.forEach(keyword => {
keyword.chart = dc.lineChart(composite)
.dimension(keywordDateDimension)
.colors(keyword.color)
.group(getReduce(keyword.word, keywordDateDimension), keyword.word)
.interpolate('basis')
composeChartsData.push(keyword.chart);
});
return composeChartsData;
}
I'm currently trying to produce a dashboard in dc.js for my master's thesis and I have hit a real roadblock today if anyone could please help it would be much appreciated. I'm new to Javascript and dc so I'll try my best to explain...
My data format (Probe Request with visible SSID):
{"vendor":"Huawei Technologies Co.Ltd","SSID":"eduroam","timestamp":"2018-07-10 12:25:26","longitude":-1.9361,"mac":"dc:d9:16:##:##:##","packet":"PR-REQ","latitude":52.4505,"identifier":"Client"}
My data format (Probe Request with Broadcast / protected SSID):
{"vendor":"Nokia","SSID":"Broadcast","timestamp":"2018-07-10 12:25:26","longitude":-1.9361,"mac":"dc:d9:16:##:##:##","packet":"PR-REQ","latitude":52.4505,"identifier":"Client"}
I'm trying to produce a bubble chart which will display vendors as a bubble (size denoted by volume of packets collected for that vendor) then plot the bubble against X axis unprotected (any SSID != broadcast) & Y axis protected (packets where "Broadcast" is in the data)
Sketch of what I mean
What I've managed to get so far
I've managed to get a bar/ row/pie charts to work as they only require me to use one dimension and run them through a group. But I think I'm fundamentally misunderstanding how to pass multiple dimensions to a group.
for each at the top adds a new value of 0 / 1 to triple if Broadcast is present in the data.
Then I'm using a custom reducer to count 0 / 1 related to unpro & pro which will be used to plot the X / Y
I've tried reworking the nasdaq example and I'm getting nowhere
Code:
queue()
.defer(d3.json, "/uniquedevices")
.await(plotVendor);
function plotVendor(error, packetsJson) {
var packets = packetsJson;
packets.forEach(function (d) {
if(d["SSID"] == "Broadcast") {
d.unpro = 0;
d.pro = 1;
} else {
d.unpro = 1;
d.pro = 0;
}
});
var ndx = crossfilter(packets);
var vendorDimension = ndx.dimension(function(d) {
return [ d.vendor, d.unpro, d.pro ];
});
var vendorGroup = vendorDimension.group().reduce(
function (p, v) {
++p.count;
p.numun += v.unpro;
p.numpr += v.pro;
return p;
},
function (p, v) {
--p.count;
p.numun -= v.unpro;
p.numpr -= v.pro;
return p;
},
function () {
return {
numun: 0,
numpr: 0
};
}
);
var vendorBubble = dc.bubbleChart("#vendorBubble");
vendorBubble
.width(990)
.height(250)
.transitionDuration(1500)
.margins({top: 10, right: 50, bottom: 30, left: 40})
.dimension(vendorDimension)
.group(vendorGroup)
.yAxisPadding(100)
.xAxisPadding(500)
.keyAccessor(function (p) {
return p.key[1];
})
.valueAccessor(function (p) {
return p.key[2];
})
.radiusValueAccessor(function (d) { return Object.keys(d).length;
})
.maxBubbleRelativeSize(0.3)
.x(d3.scale.linear().domain([0, 10]))
.y(d3.scale.linear().domain([0, 10]))
.r(d3.scale.linear().domain([0, 20]))
dc.renderAll();
};
Here is a fiddle: http://jsfiddle.net/adamistheanswer/tm9fzc4r/1/
I think you are aggregating the data right and the missing bits are
your accessors should look inside of value (that's where crossfilter aggregates)
.keyAccessor(function (p) {
return p.value.numpr;
})
.valueAccessor(function (p) {
return p.value.numun;
})
.radiusValueAccessor(function (d) {
return d.value.count;
})
your key should just be the vendor - crossfilter dimensions aren't geometric dimensions, they are what you filter and bin on:
var vendorDimension = ndx.dimension(function(d) {
return d.vendor;
});
you probably need to initialize count because ++undefined is NaN:
function () { // reduce-init
return {
count: 0,
numun: 0,
numpr: 0
};
}
Fork of your fiddle, with all the dependencies added, wrapping function disabled, and elasticX/elasticY (probably not what you want but easier to debug):
https://jsfiddle.net/gordonwoodhull/spw5oxkj/16/
I am quite stuck with an issue on a series of charts I realized with dc.js.
The issue is in a scenario similar to the Nasdaq example on the main dc.js site: a stacked chart filtered using a bar chart with the brush.
The problem is that if I keep the browser console open, after everything loads, if I try to use the brush to select a period, an error appears in the console.
In Firefox I get this:
TypeError: _x is undefined[Learn More] coordinate-grid-mixin.js:468:12
prepareXAxis coordinate-grid-mixin.js:468:12
drawChart coordinate-grid-mixin.js:1139:8
_dc/dc.coordinateGridMixin/_chart._doRedraw coordinate-grid-mixin.js:1128:8
_dc/dc.baseMixin/_chart.redraw base-mixin.js:743:21
_dc/dc.redrawAll core.js:250:8
_dc/dc.baseMixin/_chart.redrawGroup base-mixin.js:794:12
_dc/dc.coordinateGridMixin/_chart._brushing/< coordinate-grid-mixin.js:1032:16
_dc/dc.events.trigger/< events.js:34:12
While in Chrome I get this:
Uncaught TypeError: Cannot read property 'domain' of undefined
at prepareXAxis (coordinate-grid-mixin.js:468)
at drawChart (coordinate-grid-mixin.js:1139)
at Object._chart._doRedraw (coordinate-grid-mixin.js:1128)
at Object._chart.redraw (base-mixin.js:706)
at Object.dc.redrawAll (core.js:250)
at Object._chart.redrawGroup (base-mixin.js:757)
at coordinate-grid-mixin.js:1032
at events.js:34
I tried different dc.js versions and I get the same issues (from the stable 2.0.5 to the last one that has been used for the logs above 2.1.9). The only difference I got with a dc.min.js version is that instead of "undefined _x" I got an "undefined z".
Here is a picture just to get a feeling:
Now the good part of the issue, is that the charts work fine. I can visualize the data and filter it, nonetheless, the console gets soon filled up with the same error that continues to appear.
here are the more relevant parts of my code:
// a bit of copy paste to get the sense
var dateDim = ndx.dimension(function (d) { return d.date; });
var grpTime =
dateDim
.group(function (d) { return d3.time.minute(d); })
.reduce(dash_reduceAdd, dash_reduceSub, dash_reduceInit);
var minDate = d3.time.minute(dateDim.bottom(1)[0]["date"]);
var maxDate = d3.time.minute(dateDim.top(1)[0]["date"]);
stackChart
.renderArea(true)
.height(200)
.margins({top: 15, right: 50, bottom: 20, left: 40})
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.minutes)
.legend(dc.legend().x(70).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.title(stackChartTitle)
.elasticY(true)
.transitionDuration(1500)
.dimension(dateDim)
.group(grpTime, str_a)
.valueAccessor(function(d) { return d.value.val_a_Avg; })
.stack(grpTime, str_b, function(d) { return d.value.val_b_Avg; })
.ordinalColors(['#4faf00', '#5c00e6'])
.hidableStacks(true)
.rangeChart(volumeChart)
.controlsUseVisibility(true)
.brushOn(false)
;
volumeChart
.height(60)
.margins({top: 0, right: 50, bottom: 20, left: 40})
.dimension(dateDim)
.group(grpTime)
.valueAccessor(function(d) { return d.value.vAvg; })
.centerBar(true)
.gap(1)
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.minutes)
.elasticY(true)
.alwaysUseRounding(true)
.on('renderlet', function (chart) {
var rangeFilter = chart.filter();
var focusFilter = chart.focusChart().filter();
if (focusFilter && !rangeFilter) {
dc.events.trigger(function () {
chart.focusChart().replaceFilter(rangeFilter);
});
}
})
.brushOn(true)
.controlsUseVisibility(true)
;
stackChart.render();
volumeChart.render();
Any help would really be appreciated, I am not really sure how to move to fix this issue.
EDIT
I am adding the reduce functions here:
function groupArrayAdd(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.right(elements, keyfn(item));
elements.splice(pos, 0, item);
return elements;
};
}
function groupArrayRemove(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.left(elements, keyfn(item));
if(keyfn(elements[pos])===keyfn(item))
elements.splice(pos, 1);
return elements;
};
}
// Custom reduce functions
function dash_reduceAdd(p, v) {
++p.count;
// Sums
p.val_a_Sum += v.va;
p.val_b_Sum += v.vb;
// Averages
p.val_a_Avg = p.count ? p.val_a_Sum / p.count : 0;
p.val_b_Avg = p.count ? p.val_b_Sum / p.count : 0;
// Maxes
p.vaMax = groupArrayAdd(function (d) { return d.va; })(p.vaMax, v.va);
p.vbMax = groupArrayAdd(function (d) { return d.vb; })(p.vbMax, v.vb);
return p;
}
function dash_reduceSub(p, v) {
--p.count;
// Sums
p.val_a_Sum -= v.va;
p.val_b_Sum -= v.vb;
// Averages
p.val_a_Avg = p.count ? p.val_a_Sum / p.count : 0;
p.val_b_Avg = p.count ? p.val_b_Sum / p.count : 0;
// Maxes
p.vaMax = groupArrayAdd(function (d) { return d.va; })(p.vaMax, v.va);
p.vbMax = groupArrayAdd(function (d) { return d.vb; })(p.vbMax, v.vb);
return p;
}
function dash_reduceInit() {
return { count:0,
val_a_Sum:0, val_b_Sum:0,
val_a_Avg: 0, val_b_Avg: 0,
vaMax: [], vbMax:[] };
}
I found the issue causing it. It took me really quite some time. I went disassembling the code, removing one part after the other and checking if the issue persisted...
...at the end I found a really stupid problem. I forgot to remove one lineChart variable that was declared but never rendered...I havo no clue how I didn't noticed it before but anyway, after removing that chart the issue disappeared.
It was impossible here to find a solution from the code, I am answering just because it might be a clue for future people having that kind of error message.
I'm pushing two datasets into the same page.
They're both coming from separate mongodb tables, but the records are linked by a primary key 'plankey'.
I want to add a filter from this graph to the one in the second dataset.
Main chart function:
function loadProjectData(productName) {
// $('#progress_dialog').show();
buildDataLoaded = false;
$.get('/getbuildresults.json?product=' + productName, function (data) {
stats = data;
if (stats != null && stats.length > 0) {
// Convert dates to real dates
stats.forEach(function (d) {
d.builddate = new Date(d.builddate);
});
// feed it through crossfilter
ndx = crossfilter(stats);
overall = ndx.dimension(function (d) {
return d3.time.month(d.builddate);
});
overallGroup = overall.group().reduce(buildReduceAdd, buildReduceRemove, buildReduceInitial);
//Test Types Graph Data Sorter
testTypesDimension = ndx.dimension(function (d) {
return d3.time.week(d.builddate)
})
}
overallChart = dc.compositeChart("#overall-timeline-chart")
.width(chartWidth) // (optional) define chart width, :default = 200
.height(250) // (optional) define chart height, :default = 200
.transitionDuration(500) // (optional) define chart transition duration, :default = 500
.margins({
top: 10,
right: 50,
bottom: 30,
left: 40
})
.dimension(failedTestDimension)
.group(failedTestDimensionGroup)
.elasticY(true)
.mouseZoomable(false)
.elasticX(false)
.renderHorizontalGridLines(true)
.x(d3.time.scale().domain(timelineDomain))
.round(d3.time.month.round)
.xUnits(d3.time.months)
.title(function (d) {
return "Value: " + d.value;
})
.renderTitle(true)
.brushOn(true);
// Loop through available plans and create chart for each and then compose
var charts = [];
var plans = buildGroup.all();
plans.forEach(function (plan) {
charts.push(dc.lineChart(overallChart).dimension(failedTestDimension).group(failedTestDimensionGroup)
.valueAccessor(function (d) {
return d.value.result[plan.key] ? d.value.result[plan.key].failed : null;
}));
});
overallChart.compose(charts);
Second graph function (this is where I want to add the filter from the above graph):
function loadTestResultsData() {
// $('#progress_dialog').show();
testDataLoaded = false;
$.get('/gettestresults.json', function(data) {
stats = data;
if (stats != null && stats.length > 0) {
// Convert dates to real dates
stats.forEach(function (d) {
d.rundate = new Date(d.rundate);
});
// feed it through crossfilter
support_ndx = crossfilter(stats);
//Support Cases Chart
// Dimension and Group for monthly support cases
supportCasesPerMonth = support_ndx.dimension(function(d){ return d.methodName });
supportCasesPerMonthGroup = supportCasesPerMonth.group();
buildTypesChart = dc.rowChart("#failed-tests-chart")
.width(750) // (optional) define chart width, :default = 200
.height(300) // (optional) define chart height, :default = 200
.group(supportCasesPerMonthGroup) // set group
.dimension(supportCasesPerMonth) // set dimension
// (optional) define margins
.margins({
top: 20,
left: 10,
right: 10,
bottom: 20
})
// (optional) define color array for slices
.colors(['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#dadaeb'])
// (optional) set gap between rows, default is 5
.gap(7);
}
testDataLoaded = true;
dataLoaded();
});
};
You have two basic approaches. The first to be prefered.
1) Join the data first. I would suggest using something like queue
queue()
.defer(d3.json, '/getbuildresults.json?product=' + productName)
.defer(d3.json, '/gettestresults.json')
.await(ready);
function ready(error, products, stats) {
var productMap = {};
products.forEach(function (d) {
d.builddate = new Date(d.builddate);
productMap[d.plankey] = d;
});
stats.forEach(function (d) {
d.rundate = new Date(d.rundate);
$.extend(d, productMap[d.plankey]);
});
ndx = crossfilter(stats);
// build other dimensions/groups
// build charts
});
2) Your other option is to link the charts by using a trigger to filter on the plankey. So on both data sets, create a crossfilter linked dimension for plankey. Then, on the filter trigger from the second chart, see what plankeys have been set with something like
var keys = C2PlanKeysDim.all()
.filter(function(d){return d.value>0;})
.map(function(d){return d.key;});`
Then on chart 1, filter by the key on C1PlanKeysDim or whatever you call it, and trigger a chart redraw to take into account the filter.