2016-06-23 16:06:59 -04:00

166 lines
5.8 KiB
JavaScript

document.addEventListener("DOMContentLoaded", function init() {
// var config = {
// table: "tweets_nov_feb",
// valueColumn: "tweets_nov_feb.tweet_count",
// joinColumn: "tweets_nov_feb.state_abbr",
// polyTable: "states",
// polyJoinColumn: "STATE_ABBR",
// timeColumn: "tweet_time",
// timeLabel: "Number of Tweets",
// domainBoundMin: 12196,
// domainBoundMax: 32586,
// numTimeBins: 288 // 288 * 5 = number of minutes in a day.
// }
var config = {
table: "contributions",
valueColumn: "contributions.amount",
joinColumn: "contributions.contributor_zipcode",
polyTable: "zipcodes",
polyJoinColumn: "ZCTA5CE10",
timeColumn: "contrib_date",
timeLabel: "Number of Contributions",
domainBoundMin: 0,
domainBoundMax: 2600,
numTimeBins: 423
}
var con = new MapdCon().protocol("http").host("kali.mapd.com").port("9092").dbName("mapd").user("mapd").password("HyperInteractive").connect()
var crossFilter = crossfilter.crossfilter(con, config.table)
createPolyMap(crossFilter, con, dc, config)
createTimeChart(crossFilter, dc, config)
function createPolyMap(crossFilter, con, dc, config) {
var parent = document.getElementById("polymap")
// The values in the table and column specified in crossFilter.dimension
// must correspond to values in the table and keysColumn specified in polyRasterChart.polyJoin.
var dim = crossFilter.dimension(config.joinColumn) // Values to join on.
var grp = dim.group().reduceAvg(config.valueColumn) // Values to color on.
// var dim = crossFilter.dimension("tweets_nov_feb.state_abbr") // Values to join on.
// var grp = dim.group().reduceAvg("tweets_nov_feb.tweet_count") // Values to color on.
// Can use getDomainBounds to dynamically find min and max of values that will be colored,
// or the domain [min, max] can be set directly
// (in which case nesting chart creation inside this callback is unnecessary).
getDomainBounds(grp, function(domainBounds){
// Can set colorDomain directly or use domainFromBoundsAndRange to generate a .
var colorRange = ["#115f9a","#1984c5","#22a7f0","#48b5c4","#76c68f","#a6d75b","#c9e52f","#d0ee11","#d0f400"]
var colorDomain = domainFromBoundsAndRange(config.domainBoundMin, config.domainBoundMax, colorRange)
// var colorDomain = domainFromBoundsAndRange(domainBounds.min, domainBounds.max, colorRange)
var polyMap = dc
.polyRasterChart(parent, true)
.con(con)
.height(height()/1.5)
.width(width())
.dimension(dim)
.group(grp)
.tableName(config.table)
.mapUpdateInterval(750) // ms
.othersGrouper(false)
.opacity(0.90)
.mapStyle("mapbox://styles/mapbox/light-v8")
.polyJoin({table: config.polyTable, keysColumn: config.polyJoinColumn})
.colors(d3.scale.linear().domain(colorDomain).range(colorRange))
.borderColor("white")
polyMap.borderWidth(zoomToBorderWidth(polyMap.map().getZoom()))
// Keeps the border widths reasonable regardless of zoom level.
polyMap.map().on("zoom", function() {
polyMap.borderWidth(zoomToBorderWidth(polyMap.map().getZoom()))
})
dc.renderAll()
window.addEventListener("resize", _.debounce(function(){ resizeChart(polyMap, 1.5) }, 500))
})
}
function getDomainBounds (group, callback) {
var queriesFinished = {min: null, max: null}
group.bottom(1, 0, null, [maybeFinished("min")])
group.top(1, 0, null, [maybeFinished("max")])
function maybeFinished(key) {
return function (result) {
queriesFinished[key] = extractResult(result)
if(_.every(queriesFinished, function (val) { return val !== null })){
callback(queriesFinished)
}
}
}
}
function extractResult (result) {
return result[0].val
}
function domainFromBoundsAndRange (min, max, range) {
return _.range(0, range.length).map((_, i) => min + Math.round(i * max / (range.length - 1)))
}
function zoomToBorderWidth (zoomLevel) {
var MIN_ZOOM = 0.8626373575587937
var ZOOM_BORDER_DIVISOR = 20
return zoomLevel / ZOOM_BORDER_DIVISOR - MIN_ZOOM / ZOOM_BORDER_DIVISOR
}
function createTimeChart(crossFilter, dc, config) {
var timeChartMeasures = [
{expression: config.timeColumn, agg_mode: "min", name: "minimum"},
{expression: config.timeColumn, agg_mode: "max", name: "maximum"}
]
var timeChartBounds = crossFilter // TODO sync use getDomainBounds instead.
.groupAll()
.reduce(timeChartMeasures)
.values(true)
var timeChartDimension = crossFilter.dimension(config.timeColumn)
var timeChartGroup = timeChartDimension
.group()
.reduceCount("*")
.setBinParams({
numBins: config.numTimeBins,
binBounds: [timeChartBounds.minimum, timeChartBounds.maximum]
})
var timeChart = dc.lineChart("#timechart")
.width(width())
.height(height()/2.5)
.elasticY(true)
.renderHorizontalGridLines(true)
.brushOn(true)
.xAxisLabel("Time")
.yAxisLabel(config.timeLabel)
.dimension(timeChartDimension)
.group(timeChartGroup)
timeChart.x(d3.time.scale.utc().domain([timeChartBounds.minimum, timeChartBounds.maximum]))
timeChart.yAxis().ticks(5)
timeChart.xAxis().orient("top")
dc.renderAll()
window.addEventListener("resize", _.debounce(function () { resizeChart(timeChart, 2.5) }, 500))
}
function width () {
return document.documentElement.clientWidth - 30
}
function height () {
return (Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 200)
}
function resizeChart (chart, heightDivisor) {
if(typeof chart.map === "function"){
chart.map().resize()
chart.isNodeAnimate = false
}
chart
.width(width())
.height(height()/heightDivisor)
.render()
dc.renderAll()
}
})