Jonathan Huang e89bbab693 add tests for raster point layer
progress on sizing spec

finish new point layer genVega

working point raster layer with vega lite spec

fix popup behavior on raster point layer

fix examples

fix examples

add limit to point vega

update transform api and add sampling

update raster poly layer to use setState vega-lite api
2017-11-28 08:21:31 -05:00

188 lines
6.3 KiB
JavaScript

document.addEventListener("DOMContentLoaded", function init() {
var config = {
table: "contributions_donotmodify",
valueColumn: "contributions_donotmodify.amount",
joinColumn: "contributions_donotmodify.contributor_zipcode",
polyTable: "zipcodes",
polyJoinColumn: "ZCTA5CE10",
timeColumn: "contrib_date",
timeLabel: "Number of Contributions",
domainBoundMin: 0,
domainBoundMax: 2600,
numTimeBins: 423
}
new MapdCon()
.protocol("https")
.host("metis.mapd.com")
.port("443")
.dbName("mapd")
.user("mapd")
.password("HyperInteractive")
.connect(function(error, con) {
crossfilter.crossfilter(con, ["contributions_donotmodify", "zipcodes"], [{
table1: "contributions_donotmodify",
attr1: "contributor_zipcode",
table2: "zipcodes",
attr2: "ZCTA5CE10"
}]).then(function(cf) {
crossfilter.crossfilter(con, "contributions_donotmodify").then(cf2 => {
createPolyMap(cf, con, dc, config, cf2)
createTimeChart(cf, dc, config, cf2)
})
})
})
function createPolyMap(crossFilter, con, dc, config, cf2) {
window.cf = crossFilter
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("zipcodes.rowid") // Values to join on.
var grp = dim.group().reduceAvg("contributions_donotmodify.amount", "avgContrib") // 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(config.valueColumn, cf2.groupAll(), 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.minimum, domainBounds.maximum, colorRange)
var mapboxToken = "pk.eyJ1IjoibWFwZCIsImEiOiJjaWV1a3NqanYwajVsbmdtMDZzc2pneDVpIn0.cJnk8c2AxdNiRNZWtx5A9g";
var polyMap = dc
.rasterChart(parent, true)
.con(con)
.height(height()/1.5)
.width(width())
.mapUpdateInterval(750) // ms
.mapStyle("mapbox://styles/mapbox/light-v8")
.mapboxToken(mapboxToken) // need a mapbox accessToken for loading the tiles
var polyLayer = dc
.rasterLayer("polys")
.crossfilter(crossFilter)
.setState({
data: [
{
table: "contributions_donotmodify",
attr: "contributor_zipcode"
}, {
table: "zipcodes",
attr: "ZCTA5CE10"
}
],
transform: {
limit: 1000000
},
mark: {
type: "poly",
strokeColor: "white",
strokeWidth: 0,
lineJoin: "miter",
miterLimit: 10
},
encoding: {
color: {
type: "quantitative",
aggregrate: "AVG(contributions_donotmodify.amount)",
domain: colorDomain,
range: colorRange
}
}
})
polyMap.pushLayer("polys", polyLayer).init().then(() => {
// 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.renderAllAsync()
window.addEventListener("resize", _.debounce(function(){ resizeChart(polyMap, 1.5) }, 500))
})
})
}
function getDomainBounds (column, groupAll, callback) {
groupAll.reduce([
{expression: column, agg_mode: "min", name: "minimum"},
{expression: column, agg_mode: "max", name: "maximum"}
]).valuesAsync(true).then(callback)
}
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, cf2) {
getDomainBounds(config.timeColumn, cf2.groupAll(), function(timeChartBounds){
var timeChartDimension = crossFilter.dimension(config.timeColumn)
var timeChartGroup = timeChartDimension
.group()
.reduceCount("*")
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)
.binParams({
numBins: config.numTimeBins,
binBounds: [timeChartBounds.minimum, timeChartBounds.maximum]
})
timeChart.x(d3.time.scale.utc().domain([timeChartBounds.minimum, timeChartBounds.maximum]))
timeChart.yAxis().ticks(5)
timeChart
.xAxis()
.scale(timeChart.x())
.tickFormat(dc.utils.customTimeFormat)
.orient('bottom');
dc.renderAllAsync()
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)
.renderAsync()
dc.redrawAllAsync()
}
function noop () {}
})