heavyai-charting/example/example1.html
2016-03-17 12:03:01 -07:00

536 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>MapD</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="css/mapdc.min.css" />
<link rel="stylesheet" type="text/css" href="css/chart.min.css" />
<link href='https://fonts.googleapis.com/css?family=Roboto:400,700,300' rel='stylesheet' type='text/css'>
<style>
.title {
font-weight: bold;
text-align:center;
}
.mapd{
position: relative;
top: 2px;
}
.data-count{
padding-right:20px;
}
.filter-count{
font-weight: bold;
color: #45B1E8;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header" style="margin-top:10px">
<img alt="Brand" src="images/favicon.png" height="30" width="30">
<span class="mapd">MapD Demo</span>
</div>
<div class="navbar-text navbar-right">
<div class="data-count">
<span class="total"><span><span class="filter-count"></span></span>
<span>of <span class="total-count"></span> flights</span>
</div>
</div>
</div>
</nav>
<div class="main-container">
<div class="col-xs-6">
<div class="title"> Total Number of Flights by State </div>
<div class="chart1-example"></div>
</div>
<div class="col-xs-6">
<div class="title"> Carrier Departure Delay by Arrival Delay <small>(Minutes)</small> </div>
<div class="chart2-example"></div>
</div>
<div class="col-xs-12">
<div class="title"> Number of Flights by Departure Time </div>
<div class="chart3-example"></div>
</div>
</div>
<script src="node_modules/lodash/lodash.js"></script>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/d3/d3.min.js"></script>
<script src="node_modules/@mapd/mapd-con/dist/thrift.js"></script>
<script src="node_modules/@mapd/mapd-con/dist/mapd.thrift.js"></script>
<script src="node_modules/@mapd/mapd-con/dist/mapd_types.js"></script>
<script src="node_modules/@mapd/mapd-con/dist/MapdCon.js"></script>
<script src="js/mapd-crossfilter.js"></script>
<script src="js/mapdc.js"></script>
<script src="node_modules/@mapd/mapd-mapbox-gl-js/dist/mapbox-gl.js"></script>
<script src="node_modules/@mapd/mapd-mapbox-gl-js/dist/mapboxgl-overrides.js"></script>
<script>
/*
* This is example code that shows how to make 3 cross-filtered charts with the
* dc.mapd.js API. This example is not meant to be a replacement for dc.js
* documentation. For the dc.js API docs, see here
* - https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md.
* For an annotated example of using dc.js - see here:
* https://dc-js.github.io/dc.js/docs/stock.html.
*/
function createCharts(crossFilter) {
var colorScheme = ["#22A7F0", "#3ad6cd", "#d4e666"]
var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0) - 50
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 200
/*
* crossFilter is an object that handles cross-filtered the different
* dimensions and measures that compose a dashboard's charts.
* It has a number of methods on it.
*/
/*
* getColumns() will grab all columns from the table along with metadata about
* those columns.
*/
var allColumns = crossFilter.getColumns();
/*-------------------BASIC COUNT ON CROSSFILTER---------------------------*/
/*
* A basic operation is getting the filtered count and total count
* of crossFilter. This performs that operation. It is built into DC.
* Note that for the count we use crossFilter itself as the dimension.
*/
var countDim = crossFilter;
var countGroup = crossFilter.groupAll();
dc.dataCount(".data-count")
.dimension(countDim)
.group(countGroup);
/*------------------------CHART 1 EXAMPLE------------------------------*/
/*
* In crossfilter dimensions can function as what we would like to "group by"
* in the SQL sense of the term. We'd like to create a bar chart of number of
* flights by destination state ("dest_state") - so we create a crossfilter dimension
* on "dest_state"
*
* Here lies one of the chief differences between crossfilter.mapd.js and the
* original crossfilter.js. In the original crossfilter you could provide
* javascript expressions like d.dest_state.toLowerCase() as part of
* dimension, group and order functions. However since ultimately our
* dimensions and measures are transformed into SQL that hit our backend, we
* require string expressions. (i.e "extract(year from dep_timestamp))"
*/
var rowChartDimension = crossFilter.dimension("dest_state");
/*
* To group by a variable, we call group() on the function and then specify
* a "reducer". Here we want to get the count for each state, so we use the
* crossfilter reduceCount() method.
*
* More crossfilter Methods here:
* https://github.com/square/crossfilter/wiki/API-Reference#dimension
* https://github.com/square/crossfilter/wiki/API-Reference#group-map-reduce
* https://github.com/square/crossfilter/wiki/API-Reference#group_reduceCount
*/
var rowChartGroup = rowChartDimension.group().reduceCount();
/*
* We create a horizontal bar chart with the data specified above (count by destination
* state) by using a dc.rowChart (i.e. a horizontal bar chart)
*
* We invoke the following options on the rowChart using chaining.
*
* Height and width - match the containing div
*
* elasticX - a dc option to cause the axis to rescale as other filters are
* applied
*
* cap(20) - Only show the top 20 groups. By default crossFilter will sort
* the dimension expression (here, "dest_state"), by the reduce expression (here, count),
* so we end up with the top 20 destination states ordered by count.
*
* othersGrouper(false) - We only would like the top 20 states and do not want
* a separate bar combining all other states.
*
* ordinalColors(colorScheme) - we want to color the bars by dimension, i.e. dest_state,
* using the color ramp defined above (an array of rgb or hex values)
*
* measureLabelsOn(true) - a mapd.dc.js add-on which allows not only the dimension
* labels (i.e. Texas) to be displayed but also the measures (i.e. the number
* of flights with Texas as dest_state)
*
* Simple Bar Chart Example using DC api here:
* https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md
*/
var dcBarChart = dc.rowChart('.chart1-example')
.height(h/1.5)
.width(w/2)
.elasticX(true)
.cap(20)
.othersGrouper(false)
.ordinalColors(colorScheme)
.measureLabelsOn(true)
.dimension(rowChartDimension)
.group(rowChartGroup);
/*--------------------------CHART 2 EXAMPLE------------------------------*/
/*
* Bubble Chart Example:
* Here we will create a bubble chart (scatter plot with sized circles).
* We want to make a circle for each airline carrier - i.e. group by
* carrier ("carrier_name" in the dataset), with the x coordinate
* corresponding to average departure delay ("depdelay"), the y coordinate
* corresponding to average arrival delay ("arrdelay"), and the size of the
* circle corresponding to the number of flights for that carrier (the count).
* We will color by the group or key, i.e. carrier_name.
*
*/
var scatterPlotDimension = crossFilter.dimension("carrier_name");
/*
* MapD created a reduceMulti function in order to handle multiple measures.
* It takes an array of objects, each corresponding to a measure.
* Each measure object requires 3 arguments:
* 'expression' which is the measure
* 'agg_mode' which is the calculation to perform.
* 'name' is how to reference the data
*
*/
var reduceMultiExpression1 = [{
expression: "depdelay",
agg_mode:"avg",
name: "x"
},
{
expression: "arrdelay",
agg_mode:"avg",
name: "y"
},
{
expression: "*",
agg_mode:"count",
name: "size"
}];
var popupHeader = [
{ type: "dimension", label: 'carrier_name' },
{ type: "measure", label: 'depdelay', alias: 'x' },
{ type: "measure", label: 'arrdelay', alias: 'y' },
];
/*
* Note the order("size") setter here. By default the bubble chart uses the
* top function which sorts all measures in descending order. This would
* cause the us to take the top n (specified by cap) sorted by x, y, and
* size in descending order. Since we probably do not want to sort
* primarility by departure delay, we override the sort and sort by size
* instead, which corresponds to the count measure - i.e. we take the
* n most popular airlines
*/
var scatterPlotGroup = scatterPlotDimension
.group()
.reduce(reduceMultiExpression1)
.order("size")
/* We create the bubble chart with the following parameters:
*
* Width and height - as above
*
* renderHorizontalGridLines(true)
*
* renderVerticalGridLines(true) - create grid under points
*
* cap(15) - only show top 15 airlines
*
* othersGrouper(false) - do not have a bubble for airlines not in top 15
*
* **Note for all accessors below the variables correspond to variables
* defined in reduceMulti above**
*
* keyAccessor - specify variable in result set associated with key (x-axis in
* bubble chart)
*
* valueAccessor - specify variable in result set associated with value (y-axis in bubble chart)
*
* radiusValueAccessor - specify variable in result set associated with radius of the bubbles
*
* colorAccessor - specify variable in result set associated with color of the
* bubbles. Here we are not coloring by a measure but instead by the groups
* themselves so we specify the first (and only) key, key0, If we were
* grouping by multiple (N) attributes we would have key0, key1... keyN
*
* maxBubbleRelativeSize(0.04) - specifies the max radius relative to length
* of the x axis. This means we cap the bubble radius at 4% of the length of
* the x axis.
*
* transitionDuration(500) - DC (via D3) will animate movement of the points
* between filter changes. This specifies that the animation duration should
* be 500 ms.
*
* xAxisLabel, yAxisLabel - specify the labels of the charts
*
* elasticX(true), elasticY(true) - allow the axes to readjust as filters are
* changed
*
* xAxisPadding('15%'), yAxisPadding('15%') - Without padding the min and max
* points for the x and y scales will be on the edge of the graph. This tells
* the chart to add an extra 15% margin to the axes beyond the min and max of
* that axis
*
* ordinalColors(colorScheme) - we want to color the bars by dimension, i.e. dest_state,
* using the color ramp defined above (an array of rgb or hex values)
*/
dcScatterPlot = dc.bubbleChart('.chart2-example')
.width(w/2)
.height(h/1.5)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.cap(15)
.othersGrouper(false)
.keyAccessor(function (d) {
return d.x;
})
.valueAccessor(function (d) {
return d.y;
})
.radiusValueAccessor(function (d) {
return d.size;
})
.colorAccessor(function(d) {
return d.key0;
})
.maxBubbleRelativeSize(0.04)
.transitionDuration(500)
.xAxisLabel('Departure Delay')
.yAxisLabel('Arrival Delay')
.setPopupHeader(popupHeader)
.elasticX(true)
.elasticY(true)
.xAxisPadding('15%')
.yAxisPadding('15%')
.ordinalColors(colorScheme)
.dimension(scatterPlotDimension)
.group(scatterPlotGroup);
/* We create the bubble chart with the following parameters:
* dc.mapd.js allows functions to be applied at specific points in the chart's
* lifecycle. Here we want to re-adjust our chart's x,y and r (radius) scales
* as data is filtered in an out to take into account the changing range of
* the data along these different measures. Here we set the charts scale
* using standard d3 functions - telling dc.mapd.js to do this before each
* render and redraw */
var setScales = function(chart, type){
chart.on(type, function(chart) {
chart.x(d3.scale.linear().domain(d3.extent(chart.data(), chart.keyAccessor())));
chart.y(d3.scale.linear().domain(d3.extent(chart.data(), chart.valueAccessor())));
chart.r(d3.scale.linear().domain(d3.extent(chart.data(), chart.radiusValueAccessor())));
});
}
setScales(dcScatterPlot, "preRender");
setScales(dcScatterPlot, "preRedraw");
/*---------------------TIME CHART (CHART 3) EXAMPLE------------------------------*/
/*
* First we want to determine the extent (min,max) of the time variable so we
* can set the bounds on the time chart appropriately.
*
* If you know the bounds a priori you can do this manually but here we will
* do it dymaically via a query sent to the backend through the crossfilter
* api.
*
* We create a reduceMulti expression that will get the min and max of the
* variable dep_timestamp.
*
*/
var reduceMultiExpression2 =
[{
expression: "dep_timestamp",
agg_mode:"min",
name: "min"
},
{
expression: "dep_timestamp",
agg_mode:"max",
name: "max"}
]
/* Note than when we are doing aggregations over the entire dataset we use
* the crossfilter object itself as the dimension with the groupAll method
*
* values(true) gets the values for our groupAll measure (here min and max
* of dep_timestamp) - true means to ignore currently set filters - i.e.
* get a global min and max
*/
var timeChartBounds = crossFilter
.groupAll()
.reduceMulti(reduceMultiExpression2)
.values(true);
var timeChartDimension = crossFilter.dimension("dep_timestamp");
/* We would like to bin or histogram the time values. We do this by
* invoking setBinParams on the group. Here we are asking for 400 equal
* sized bins from the min to the max of the time range
*/
var timeChartGroup = timeChartDimension.group()
.reduceCount()
.setBinParams({
numBins: 400,
binBounds: [timeChartBounds.min,timeChartBounds.max]
});
/* We create the time chart as a line chart
* with the following parameters:
*
* Width and height - as above
*
* elasticY(true) - cause the y-axis to scale as filters are changed
*
* renderHorizontalGridLines(true) - add grid lines to the chart
*
* brushOn(true) - Request a filter brush to be added to the chart - this
* will allow users to drag a filter window along the time chart and filter
* the rest of the data accordingly
*
*/
var dcTimeChart = dc.lineChart('.chart3-example')
.width(w)
.height(h/2.5)
.elasticY(true)
.renderHorizontalGridLines(true)
.brushOn(true)
.xAxisLabel('Departure Time')
.yAxisLabel('# Flights')
.dimension(timeChartDimension)
.group(timeChartGroup);
/* Set the x and y axis formatting with standard d3 functions */
dcTimeChart
.x(d3.time.scale.utc().domain([timeChartBounds.min,timeChartBounds.max]))
.yAxis().ticks(5);
dcTimeChart
.xAxis().orient('top');
/* Calling dc.renderAll() will render all of the charts we set up. Any
* filters applied by the user (via clicking the bar chart, scatter plot or dragging the time brush) will automagically call redraw on the charts without any intervention from us
*/
dc.renderAll()
/*--------------------------RESIZE EVENT------------------------------*/
/* Here we listen to any resizes of the main window. On resize we resize the corresponding widgets and call dc.renderAll() to refresh everything */
window.addEventListener("resize", debounce(reSizeAll, 100));
function reSizeAll(){
var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0) - 50
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 200
dcBarChart
.height(h/1.5)
.width(w/2)
dcScatterPlot
.height(h/1.5)
.width(w/2)
dcTimeChart
.width(w)
.height(h/2.5)
dc.renderAll();
}
}
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
function init() {
/*
* mapdcon is a monad.
*
* It provides a MapD specific API to Thrift. Thrift is used to connect to our
* database backend.
*/
/* Before doing anything we must set up a mapd connection, specifying
* username, password, host, port, and database name */
var con = new MapdCon()
.protocol("http")
.host("kali.mapd.com")
.port("9092")
.dbName("mapd")
.user("mapd")
.password("HyperInteractive")
.connect();
/*
* This instaniates a new crossfilter.
* Pass in mapdcon as the first argument to crossfilter, then the
* table name, then a label for the data (unused in this example).
*
* to see all availables -- con.getTables()
*/
var crossFilter = crossfilter(con, "flights");
/*
* Pass instance of crossfilter into our reateCharts.
*/
createCharts(crossFilter);
}
document.addEventListener('DOMContentLoaded', init, false);
</script>
</body>
</html>