dc.js - grouping data using UTC time - d3.js

I have 2 issues i need help with...
When using the range chart to select individual days the data table updates accordingly, but when i extend the range to again include all records i can't seem to select the last day?
UTC time issue, i seem to have to change the time to 5pm to have the days display properly if i don't the days seem to shift down a day along with their associated values. See line 85
to be clearer with regards to problem #2
If i use 2010-01-02T00:00:00Z it will map incorrectly to Jan 1st.
if i use 2010-01-02T05:00:00Z it works correctly mapping to jan 2.
I have this posted live on Plunker... see here:
http://plnkr.co/edit/3nPjiE08ZzRXgNmSeym5?p=preview
Any help would be greatly appreciated! Cheers Bruce
<!DOCTYPE html>
<html lang="en">
<head>
<title>Chart</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="http://www.digitalliquid.com/tank/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="http://www.digitalliquid.com/tank/dc.css" />
<style>
#monthly-volume-chart g.y {
display: none;
}
#logo {
margin-right: 2em;
margin-top: 2em;
}
</style>
</head>
<body>
<!-- <div class="container-fluid"> -->
<div class="container">
<div class="row">
<div id="monthly-move-chart">
<strong>Risk History</strong>
<span class="reset" style="display: none;">range: <span class="filter"></span></span>
<a class="reset" href="javascript:moveChart.filterAll();volumeChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<div class="clearfix"></div>
</div>
</div>
<div class="row">
<div id="monthly-volume-chart">
</div>
<p class="muted pull-right" style="margin-right: 15px;">select a time range to zoom in</p>
</div>
<div class="row">
<div>
<div class="dc-data-count">
<span class="filter-count"></span> selected out of <span class="total-count"></span> records | <a href="javascript:dc.filterAll();
dc.renderAll();">Reset All</a>
</div>
</div>
<table class="table table-hover dc-data-table">
</table>
</div>
<div class="clearfix"></div>
</div>
<script type="text/javascript" src="http://www.digitalliquid.com/tank/d3.js"></script>
<script type="text/javascript" src="http://www.digitalliquid.com/tank/crossfilter.js"></script>
<script type="text/javascript" src="http://www.digitalliquid.com/tank/dc.js"></script>
<script type="text/javascript" src="http://www.digitalliquid.com/tank/colorbrewer.js"></script>
<script type="text/javascript">
'use strict';
var moveChart = dc.lineChart('#monthly-move-chart');
var volumeChart = dc.barChart('#monthly-volume-chart');
var nasdaqCount = dc.dataCount('.dc-data-count');
var nasdaqTable = dc.dataTable('.dc-data-table');
String.prototype.replaceAt = function(index, character) {
return this.substr(0, index) + character + this.substr(index + character.length);
}
// Load Data /Fix date
d3.json('risk.json', function(data) {
var dateFormat = d3.time.format.utc("%Y-%m-%dT%H:%M:%SZ");
data.forEach(function(d) {
// i seem to have to change the time to 5pm to have the days display properly
//if i don't the days seem to shift down with their associated values??????
// comment out the line below to see what i mean
// change hour in date to 5 pm
d.date = d.date.replaceAt(12, "5");
d.dd = dateFormat.parse(d.date);
d.month = d3.time.month(d.dd);
d.day = d3.time.day(d.dd);
d.risk = +d.risk; // coerce to number
});
console.log(JSON.stringify(data));
// ### Create Crossfilter Dimensions and Groups******************
var ndx = crossfilter(data);
var totalReadings = ndx.size();
var all = ndx.groupAll();
// var brush = d3.svg.brush();
// Dimension by full date
var dateDimension = ndx.dimension(function(d) {
return d.dd;
});
// Dimension by risk
var riskdimension = ndx.dimension(function(d) {
return d.risk;
});
// Dimension by day
var daydim = ndx.dimension(function(d) {
return d.day;
});
var mygroup = daydim.group().reduce(
function(p, v) {
++p.days;
// p.total += (v.open + v.close) / 2;
// p.avg = Math.round(p.total / p.days);
// return v.risk;
p.risk = v.risk
return p;
},
function(p, v) {
--p.days;
// p.total -= (v.open + v.close) / 2;
// p.avg = p.days ? Math.round(p.total / p.days) : 0;
// return v.risk;
p.risk = v.risk
return p;
},
function() {
return {
risk: 0,
};
}
);
//### Define Chart Attributes
moveChart
.renderArea(true)
.width(960)
.height(200)
.transitionDuration(1000)
.margins({
top: 30,
right: 50,
bottom: 25,
left: 40
})
.dimension(daydim)
.group(mygroup, 'Risk')
.mouseZoomable(true)
.rangeChart(volumeChart)
.y(d3.scale.linear().domain([0, 100]))
.x(d3.time.scale().domain([data[0].dd, data[data.length - 1].dd]))
.round(d3.time.day.round)
.xUnits(d3.time.days)
.renderHorizontalGridLines(true)
// ##### Legend ----------------------------------------------------------
.legend(dc.legend().x(880).y(10).itemHeight(13).gap(5))
.brushOn(false)
.valueAccessor(function(d) {
return d.value.risk;
})
// #### Volume Chart ----------------------------------------------------------------------------------------------------------------------
volumeChart
.width(960)
.height(40)
.margins({
top: 0,
right: 50,
bottom: 20,
left: 40
})
.dimension(daydim)
.group(mygroup, 'Risk')
.centerBar(true)
.gap(1)
.x(d3.time.scale().domain([data[0].dd, data[data.length - 1].dd]))
.round(d3.time.day.round)
.alwaysUseRounding(true)
.xUnits(d3.time.days)
.valueAccessor(function(d) {
return d.value.risk;
});
//#### Data Count ***********************************
nasdaqCount /* dc.dataCount('.dc-data-count', 'chartGroup'); */
// dc.dataCount(".dc-data-count")
// dc.dataCount("#monthly-move-chart")
.dimension(ndx)
.group(all);
// .html({
// some: '<strong>%filter-count</strong> selected out of <strong>%total-count</strong> records' +
// ' | <a href=\'javascript:dc.filterAll(); dc.renderAll();\'\'>Reset All</a>',
// all: 'All records selected. Please click on the graph to apply filters.'
// });
//#### Data Table
nasdaqTable /* dc.dataTable('.dc-data-table', 'chartGroup') */
.dimension(dateDimension)
.group(function(d) {
var format = d3.format('02d');
return d.dd.getFullYear() + '/' + format((d.dd.getMonth() + 1));
})
.size(100)
.columns(['date', 'risk', ])
.sortBy(function(d) {
return d.dd;
})
// (_optional_) sort order, `default = d3.ascending`
.order(d3.ascending)
// (_optional_) custom renderlet to post-process chart using [D3](http://d3js.org)
.on('renderlet', function(table) {
table.selectAll('.dc-table-group').classed('info', true);
});
dc.renderAll();
});
</script>
</body>
</html>

By default all times will be interpreted in the local time zone.
If you want to group your data using UTC time, you should use the UTC versions of the d3 time interval utilities.
So, everywhere you use d3.time.days that should be d3.time.days.utc, etc.
We use these in the dc.js Jasmine tests to ensure the tests run consistently in any time zone.
It will probably take some iteration to shake out all the places that are assuming the local time zone. I have fixed a few more in my fork of your plunker:
http://plnkr.co/edit/qX5WvAeLxaO6kq6tqJ4C?p=preview
First, you probably also need to use d3.time.day.utc (note the singular form) in your dimension function, instead of reading the .dd field, which will use the local time zone:
// Dimension by day
var daydim = ndx.dimension(function(d) {
return d3.time.day.utc(d.dd);
});
(This may not matter in your case, if the data is not more granular than a day, but it will matter if you need to group multiple times within a day.)
Next, anything that prints the dates is going to screw them up again. So, the title function:
moveChart
.title(function(d) { return d.key.toUTCString() + ': ' + d.value; })
Next, the filter printer. Here you probably want to use d3 time formatting, I printed the details in order to debug the brushing issue mentioned below:
.filterPrinter(function(f) {
return f[0][0].toUTCString() + ' - ' + f[0][1].toUTCString()
});
Finally, even though the chart is showing the whole last day, you have actually set the end of the range to the beginning of the first day, so it can't include the last data point.
There is probably a bug in dc.js here (or some confusing features), but we'll ignore that and just add a second to the end of the range:
var endDate = new Date(data[data.length - 1].dd);
endDate.setSeconds(endDate.getSeconds()+1)
.x(d3.time.scale.utc().domain([data[0].dd, endDate]))

Related

Scrollspy Uikit 3 framework and count up

i am a beginer in frontend and javascript hard for me :( I want to make count numbers with component scrollspy uikit 3 without jquery https://getuikit.com/docs/scrollspy
I made like this, but it's not working
<span id="number"></span>
<script>
UIkit.scrollspy('#number', 'inview', function () {
const countUp = new CountUp('number', 0, 1000 );
countUp.start();
});
</script>
I created a script for this.
var util = UIkit.util;
var el = util.$('#heading');
var textIndex = 0;
UIkit.scrollspy(el, {repeat: true, delay: 100});
util.on(el,'inview', function (){
function counter( start, end, duration) {
let current = start,
range = end - start,
increment = end > start ? 1 : -1,
step = Math.abs(Math.floor(duration / range)),
timer = setInterval(() => {
current += increment;
el.textContent = current;
if (current == end) {
clearInterval(timer);
}
}, step);
}
counter(0, 100, 3000);
});
util.on(el, 'outview', function(){
el.textContent = 0;
});
<!-- UIkit CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit#3.6.22/dist/css/uikit.min.css" />
<!-- UIkit JS -->
<script src="https://cdn.jsdelivr.net/npm/uikit#3.6.22/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit#3.6.22/dist/js/uikit-icons.min.js"></script>
<section class="uk-section uk-section-primary" uk-height-viewport="offset-top: true">
<div class="uk-container">
<div class="uk-text-center">
<h1 >SECTION 1</h1>
Source Code for Number Increment
</div>
<div class="uk-text-center uk-margin-medium-top">
<a class="uk-link-reset" href="#section" uk-scroll>Scroll</a>
</div>
</div>
</section>
<section class="uk-section uk-section-secondary" id="section">
<div class="uk-container">
<h1 class="uk-text-center" id="heading">0</h1>
</div>
</section>
You need to listen inview and outview event. Here is the method:
UIkit.util.on(element, 'event', function(){
//your code will be here here
});

responsive dc.js chart is taking over the whole window

I'm having trouble making my chart responsive, I'm trying to apply the same example with DC.js and Crossfilter that is at this link: resizing-series.
The chart is responsive, however, there are some bugs when interacting with the tables I have. For example, when I click on either the graph or any of the tables, the graph becomes very large, occupying the entire screen. As shown in the image below:
When I leave the screen split with Console, the graph is rendered responsively, as I change the screen size the size of the graph is also changed, however, if I leaveConsole the graph remains occupying the entire the screen and does not return to the default initial size shown in the first image.
Does anyone know how to tell me how to fix these bugs? I couldn't understand why this is happening.
Thnaks in advance.
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(600)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Cities Table
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
var adjustX = 10,
adjustY = 40;
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(window.innerHeight - adjustY);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(window.innerHeight - adjustY);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>12</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10">
<div class="col-md-4">
<div id="composite"></div>
</div>
</div>
</div>
<div class="row content">
<div class="col-md-10">
<div style="padding: 20px;;" class="row marginClass">
<h4 class="pull-left" id="Introduction">
<small>Fictitious company data | Drilldown Example |</small>
</h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selected from
<span class="total-count"></span> records |
<a id="reset"> Reset </a>
</span>
</h6>
</div>
<div class="col-md-4">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-4">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
Thanks for posting an example! The bug doesn't show up in the SO code snippet feature unless you go to full page mode. I found your fiddle easier to work with.
Note: this is a complete rewrite of my previous answer, which was not clear.
1. Using ResizeObserver
updated 4/28/20
As of Safari 13.1 (released March 24 2020) all modern browsers support ResizeObserver. This is the cleanest way to detect chart resizing.
I recommend
Use a top-down layout such as flexbox or grid to position the divs for your charts
Use ResizeObserver to determine when the div has changed size
Tell the charts to detect the chart div size using .width(null).height(null)
There is currently one resizing example that does this.
The special value null tells the chart to make the SVG node the same size as its parent div:
chart1.width(null)
.height(null)
The callback uses a helper function to disable transitions, because transitions just slow resizing down and make it look clunky:
const callback = chart => entries => {
redraw_chart_no_transitions(
chart
.width(null)
.height(null)
.rescale());
};
Setting up the observer looks like
new ResizeObserver(callback(chart1)).observe(d3.select('#test1').node());
Please see the example for more details.
2. Using window.onresize
The other resizing examples watch for window.onresize because until recently that was the only efficient, reliable cross-browser way to detect changes.
They calculate chart sizes based on the window size, which works well if your layout is bottom-up, e.g. using the default float: left layout.
Here is the function which sets this up:
function apply_resizing(chart, adjustX, adjustY, onresize) {
if(!Array.isArray(chart))
chart = [chart];
if(!isNaN(adjustX))
adjustX = (dx => x => x-dx)(adjustX);
adjustX = adjustX || (x => x);
if(!isNaN(adjustY))
adjustY = (dy => y => y-dy)(adjustY);
adjustY = adjustY || adjustX || (y => y);
chart.forEach(c => c.width(adjustX(window.innerWidth))
.height(adjustY(window.innerHeight)));
window.onresize = function () {
if (onresize) {
chart.forEach(onresize);
}
chart.forEach(c => {
c.width(adjustX(window.innerWidth))
.height(adjustY(window.innerHeight));
if (c.rescale) {
c.rescale();
}
});
redraw_chart_no_transitions(chart);
};
}
A single chart can be initialized like so:
apply_resizing(chart, 20);
This fills the window but makes the chart 20 pixels less wide.
The function can also take multiple charts in an array, and adjustment functions to support complicated layouts, like one where two charts should split the window vertically:
apply_resizing([heatmapChart, barChart], 20, y => y/2-fudge);

DC.js - barChart not filtered on click of geoChoroplethChart

I have a choropleth map of counties, and a bar chart showing population of each county on the map. When I click on the bar chart the map is filtered and redrawn to show the selected bar, but when I click on the a county in the choropleth map the bar chart is not filtered to show the population data. I don't understand why it would be filtering one way but not the other. Any help is appreciated!
<div id="iowa-map">
<strong>Population by counties (color: total population)</strong>
<a class="reset" href="javascript:iowaMap.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="population-chart">
<strong>Population by county</strong>
<a class="reset" href="javascript:populationChart.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 class="clearfix"></div>
<div>
Reset All
</div>
var iowaMap = dc.geoChoroplethChart("#iowa-map");
var populationChart = dc.barChart("#population-chart");
d3.csv("iowaCountiesPop.csv", function (data) {
data.forEach(function (d) {
d.county = d.county;
d.popByCounty = +d.e2015;
});
var data = crossfilter(data);
var counties = data.dimension(function (d) {
return d.county;
});
var counties2 = data.dimension(function (d) {
return d.county;
});
var popByCounty = counties.group().reduceSum(function (d) {
return d.popByCounty;
});
d3.json("IowaCounties.json", function (countiesJson) {
iowaMap.width(990)
.height(500)
.dimension(counties)
.group(popByCounty)
.projection(d3.geo.mercator()
.translate([495, 250])
.rotate([93 + 20 / 60, -41 - 60 / 60])
.scale(7900))
.colors(d3.scale.quantile().range(colorScheme[quantiles]))
.colorDomain([0, 430640])
.overlayGeoJson(countiesJson.features, "NAME", function (d) {
return d.properties.NAME;
})
.title(function (d) {
return d.key + " County \nTotal Population: " + numberFormat(d.value);
})
.on('renderlet', function(map) {
map.selectAll("path").on("click", function(d) {
//console.log("click!", d)
map.filter(d.properties.NAME)
.redrawGroup();
})
});
populationChart.width(width)
.height(height)
.dimension(counties2)
.group(popByCounty)
.x(d3.scale.ordinal().domain(counties))
.xUnits(dc.units.ordinal)
.margins({top: 0, right: 0, bottom: 70, left: 70})
.yAxisLabel(["Population Values"])//,[12])
.xAxisLabel("County Names")
.barPadding(0.1)
.outerPadding(0.05)
.elasticY(false)
//.turnOnControls(true)
.on('renderlet', function(chart) {
chart.selectAll('rect').on("click", function(d) {
//console.log("click!", d)
chart.filter(d.data.key)
.redrawGroup();
})
chart.selectAll("g.x text")
.style("text-anchor", "end")
.attr("dx","-8")
.attr("dy", "5")
.attr("transform", "rotate(-50)");
});
dc.renderAll();
});
});
So what is the the Most Frequently Asked Question Ever for dc.js?
Why is my chart not filtering?
And what is the most frequent answer?
Your charts are on the same dimension, or more specifically, your second chart's group is observing the same dimension that the first chart is filtering. Which means that it will not see the changes, because groups do not observe their own dimensions.
It looks like you started to go in this direction, but only duplicated the dimension. Both charts are observing the same group, and since that group is produced from the dimension for the map, it will not observe filtering on the map.
iowaMap
.dimension(counties)
.group(popByCounty)
populationChart
.dimension(counties2)
.group(popByCounty)
Instead:
function pop_by_county(dim) {
return dim.group().reduceSum(function(d) {
return d.popByCounty;
});
}
var popByCounty = pop_by_county(counties),
popByCounty2 = pop_by_county(counties2);
populationChart
.dimension(counties2)
.group(popByCounty2)
https://jsfiddle.net/gordonwoodhull/28qsa0jr/7/

I don't know how to change a color in a d3.js US map when hovered over

I have this map of the USA. I know how to bind other data, change colors of states but the only thing I don't know how to change the default orangish color when a state is hovered over.
You can find it here as well: http://blockbuilder.org/malcolm-decuire/34d2ce39d3b8c2f8a577
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://datamaps.github.io/scripts/datamaps.all.min.js?v=1"></script>
<h2> American Lynches | 1830-1970</h2>
<div id="map" style="position: relative; width: 900px; max-height: 900px;"></div>
<script>
var map = new Datamap({
scope: 'usa',
element: document.getElementById('map'),
geographyConfig:{
highlightBorderColor: '#bada55',
popupTemplate: function(geography, data){
return '<div class="hoverinfo">' + geography.properties.name + ' White/Black ' + data.LynchWhite + ' / ' + data.LynchBlack
},
},
height: 500,
fills: {
'none': '#999999',
'White': '#CC4731',
'Black': '#306596',
},
data: {
"AZ": {
"fillKey": "White",
"LynchWhite": 31,
"LynchBlack": 0
},
...
}
})
//keep this code
map.bubbles([ ], {
popupTemplate: function(geography, data) {
return "<div class='hoverinfo'>It is " + data.name + "</div>";
}
});
</script>
<script>
var ordinal = d3.scale.ordinal()
.domain(["white", "black", "none"])
.range([ "rgb(204,71,49)", "rgb(48,101,150)", "rgb(153,153,153"]);
var svg = d3.select("svg");
svg.append("g")
.attr("class", "legendOrdinal")
.attr("transform", "translate(450,450)");
var legendOrdinal = d3.legend.color()
.shape("path", d3.svg.symbol().type("triangle-up").size(150)())
.shapePadding(10)
.scale(ordinal);
svg.select(".legendOrdinal")
.call(legendOrdinal);
</script>
</body>
According to the documentation, it's controlled by the highlightFillColor config option:
geographyConfig:{
highlightFillColor: '#FC8D59'
}

d3.js d3.json is not working - how to make this function work?

Trying to develop a data visualization app with d3.js
Using a local json file named "yelp_test_set_business.json"
When I attempt to use d3.json to load this data, nothing is being passed to the callback function, which, in tern, also triggers an error within crossfilter.js library.
here is my entire file:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<script src='javascript/d3.js' type='text/javascript'></script>
<script src='javascript/crossfilter.js' type='text/javascript'></script>
<script src='javascript/dc.js' type='text/javascript'></script>
<script src='javascript/jquery-1.12.1.min.js' type='text/javascript'></script>
<script type='text/javascript'>
/********************************************************
* *
* dj.js example using Yelp Kaggle Test Dataset *
* Eol 9th May 2013 *
* *
********************************************************/
/********************************************************
* *
* Step0: Load data from json file *
* *
********************************************************/
d3.json("data/yelp_test_set_business.json", function (yelp_data) {
/********************************************************
* *
* Step1: Create the dc.js chart objects & ling to div *
* *
********************************************************/
var bubbleChart = dc.bubbleChart("#dc-bubble-graph");
var pieChart = dc.pieChart("#dc-pie-graph");
var volumeChart = dc.barChart("#dc-volume-chart");
var lineChart = dc.lineChart("#dc-line-chart");
var dataTable = dc.dataTable("#dc-table-graph");
var rowChart = dc.rowChart("#dc-row-graph");
/********************************************************
* *
* Step2: Run data through crossfilter *
* *
********************************************************/
var ndx = crossfilter(yelp_data);
/********************************************************
* *
* Step3: Create Dimension that we'll need *
* *
********************************************************/
// for volumechart
var cityDimension = ndx.dimension(function (d) { return d.city; });
var cityGroup = cityDimension.group();
var cityDimensionGroup = cityDimension.group().reduce(
//add
function(p,v){
++p.count;
p.review_sum += v.review_count;
p.star_sum += v.stars;
p.review_avg = p.review_sum / p.count;
p.star_avg = p.star_sum / p.count;
return p;
},
//remove
function(p,v){
--p.count;
p.review_sum -= v.review_count;
p.star_sum -= v.stars;
p.review_avg = p.review_sum / p.count;
p.star_avg = p.star_sum / p.count;
return p;
},
//init
function(p,v){
return {count:0, review_sum: 0, star_sum: 0, review_avg: 0, star_avg: 0};
}
);
// for pieChart
var startValue = ndx.dimension(function (d) {
return d.stars*1.0;
});
var startValueGroup = startValue.group();
// For datatable
var businessDimension = ndx.dimension(function (d) { return d.business_id; });
/********************************************************
* *
* Step4: Create the Visualisations *
* *
********************************************************/
bubbleChart.width(650)
.height(300)
.dimension(cityDimension)
.group(cityDimensionGroup)
.transitionDuration(1500)
.colors(["#a60000","#ff0000", "#ff4040","#ff7373","#67e667","#39e639","#00cc00"])
.colorDomain([-12000, 12000])
.x(d3.scale.linear().domain([0, 5.5]))
.y(d3.scale.linear().domain([0, 5.5]))
.r(d3.scale.linear().domain([0, 2500]))
.keyAccessor(function (p) {
return p.value.star_avg;
})
.valueAccessor(function (p) {
return p.value.review_avg;
})
.radiusValueAccessor(function (p) {
return p.value.count;
})
.transitionDuration(1500)
.elasticY(true)
.yAxisPadding(1)
.xAxisPadding(1)
.label(function (p) {
return p.key;
})
.renderLabel(true)
.renderlet(function (chart) {
rowChart.filter(chart.filter());
})
.on("postRedraw", function (chart) {
dc.events.trigger(function () {
rowChart.filter(chart.filter());
});
});
;
pieChart.width(200)
.height(200)
.transitionDuration(1500)
.dimension(startValue)
.group(startValueGroup)
.radius(90)
.minAngleForLabel(0)
.label(function(d) { return d.data.key; })
.on("filtered", function (chart) {
dc.events.trigger(function () {
if(chart.filter()) {
console.log(chart.filter());
volumeChart.filter([chart.filter()-.25,chart.filter()-(-0.25)]);
}
else volumeChart.filterAll();
});
});
volumeChart.width(230)
.height(200)
.dimension(startValue)
.group(startValueGroup)
.transitionDuration(1500)
.centerBar(true)
.gap(17)
.x(d3.scale.linear().domain([0.5, 5.5]))
.elasticY(true)
.on("filtered", function (chart) {
dc.events.trigger(function () {
if(chart.filter()) {
console.log(chart.filter());
lineChart.filter(chart.filter());
}
else
{lineChart.filterAll()}
});
})
.xAxis().tickFormat(function(v) {return v;});
console.log(startValueGroup.top(1)[0].value);
lineChart.width(230)
.height(200)
.dimension(startValue)
.group(startValueGroup)
.x(d3.scale.linear().domain([0.5, 5.5]))
.valueAccessor(function(d) {
return d.value;
})
.renderHorizontalGridLines(true)
.elasticY(true)
.xAxis().tickFormat(function(v) {return v;}); ;
rowChart.width(340)
.height(850)
.dimension(cityDimension)
.group(cityGroup)
.renderLabel(true)
.colors(["#a60000","#ff0000", "#ff4040","#ff7373","#67e667","#39e639","#00cc00"])
.colorDomain([0, 0])
.renderlet(function (chart) {
bubbleChart.filter(chart.filter());
})
.on("filtered", function (chart) {
dc.events.trigger(function () {
bubbleChart.filter(chart.filter());
});
});
dataTable.width(800).height(800)
.dimension(businessDimension)
.group(function(d) { return "List of all Selected Businesses"
})
.size(100)
.columns([
function(d) { return d.name; },
function(d) { return d.city; },
function(d) { return d.stars; },
function(d) { return d.review_count; },
function(d) { return 'Map"}
])
.sortBy(function(d){ return d.stars; })
// (optional) sort order, :default ascending
.order(d3.ascending);
/********************************************************
* *
* Step6: Render the Charts *
* *
********************************************************/
dc.renderAll();
});
</script>
<link href='stylesheets/bootstrap.min.css' rel='stylesheet' type='text/css'>
<!--<link href='stylesheets/dc.css' rel='stylesheet' type='text/css'>-->
<!--<script src='simple_vis.js' type='text/javascript'></script>-->
</head>
<body>
<div class='container' id='main-container'>
<div class='content'>
<div class='container' style='font: 10px sans-serif;'>
<h3>Visualisation of Kaggle Yelp Test Business Data set (using dc.js)</h3>
<h4>Demo for the Dublin Data Visualisation Meetup Group</h4>
<div class='row-fluid'>
<div class='remaining-graphs span8'>
<div class='row-fluid'>
<div class='bubble-graph span12' id='dc-bubble-graph'>
<h4>Average Rating (x-axis), Average Number of Reviews (y-axis), Number of Business' (Size)</h4>
</div>
</div>
<div class='row-fluid'>
<div class='pie-graph span4' id='dc-pie-graph'>
<h4>Average Rating in Stars (Pie)</h4>
</div>
<div class='pie-graph span4' id='dc-volume-chart'>
<h4>Average Rating in Stars / Number of Reviews (Bar)</h4>
</div>
<div class='pie-graph span4' id='dc-line-chart'>
<h4>Average Rating in Stars / Number of Reviews (Line)</h4>
</div>
</div>
<!-- /other little graphs go here -->
<div class='row-fluid'>
<div class='span12 table-graph'>
<h4>Data Table for Filtered Businesses</h4>
<table class='table table-hover dc-data-table' id='dc-table-graph'>
<thead>
<tr class='header'>
<th>Name</th>
<th>City</th>
<th>Review Score (in Stars)</th>
<th>Total Reviews</th>
<th>Location</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div class='remaining-graphs span4'>
<div class='row-fluid'>
<div class='row-graph span12' id='dc-row-graph' style='color:black;'>
<h4>Reviews Per City</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
The following call:
d3.json("data/yelp_test_set_business.json", function (yelp_data) { ...}
returns nothing in yelp_data
Does anyone know why this is hapenning?
Instead of doing like this to load your JSON:
d3.json("data/yelp_test_set_business.json", function (yelp_data) {
It should have been:
d3.json("data/yelp_test_set_business.json", function (error, yelp_data) {
Read this
Most of the time, this error is related to the fact that you just open the html file in your browser, which then tries to open the json file using the file:/// protocol, resulting in a cross origin violation.
D3 doc on requests.
One way to fix it is just by using a web server to serve the .html and the .json.
If you have python installed, just go to the folder where you file is located and run python -m SimpleHTTPServer, then navigate with your browser to http://localhost:8080. This way both the .html and .json will be served from the same origin (namely localhost:8080), and you will be able to load file via d3.json, d3.csv, etc...

Resources