/// /** * Created by hanl on 6/22/2015. * * IMPORTANT: * - interface.ts: This is the master source file, in TypeScript, that defines the * interface for Singular. * - interface.js: AUTOMATICALLY GENERATED! Any changes here will be lost. * * Note, however, that you can write normal JavaScript code in the interface.ts * file -- no need to learn TypeScript! */ 'use strict'; /*global dc, d3,crossfilter,console,colorbrewer */ /** * Create Singular Chart Objects * - chart objects associated with the container elements identified by the css selector. * * Usage * */ // //var myCharts = new dc.CHARTS(); // //d3.json("assets/barchart.data.json", function (error, json) { // if (error) return console.warn(error); // ["seqlen0", "seqlen1", "seqlen2"].forEach(function (chartname, index) { // var chart = myCharts.createBarChart({ // field: chartname, // xmin: 6, // xmax: 20, // xtickscale: 1, // width: 300, // height: 200 // }).filterHandler(myCharts.defaultFilterHandler); // // chart.margins().left = 80; // // this will make the left margin to show the yaxis properly // chart.group(myCharts.getGroupsFromData(json)).render(); // }); // myCharts.renderAll() // }); // //d3.json("assets/rowchart.data.json", function (error, json) { // if (error) return console.warn(error); // ["row0", "row1", "row2"].forEach(function (chartname, index) { // var chart = myCharts.createRowChart({ // field: chartname, // width: 300, // height: 200 // }).filterHandler(myCharts.defaultFilterHandler); // chart.group(myCharts.getGroupsFromData(json)).render(); // }); // myCharts.renderAll() // }); // // //d3.json("assets/rowchart.data.json", function (error, json) { // if (error) return console.warn(error); // ["pie0", "pie1", "pie2"].forEach(function (chartname, index) { // var chart = myCharts.createPieChart({ // field: chartname, // width: 300, // height: 200 // }).filterHandler(myCharts.defaultFilterHandler); // chart.dimension(myCharts.getDimensions(json)); // chart.group(myCharts.getGroupsFromData(json)).render(); // }); // myCharts.renderAll() // }); declare module DC { export interface Base { useRemoteData:boolean; } export interface BaseMixin { load(data:any):void; xtickscale:number; } } declare var dc:DC.Base; interface ChartConfiguration { field:string ;// use to label the field width:number; height:number; itemId?:string; // dom el id, #id, must be unique if provided colors?:any[]; colorsdomain?:any[]; //Pie Chart innerRadius?:number; slicesCap?:number; dimension?:any; dimensionGroup?:any; //Bar Chart and time series Bar Chart xmin?:any; xmax?:any; gap?:number; //Bar Chart only numberFormat?:any; xtickscale?:number; elasticX:boolean; //time series Bar Chart Only xUnits?:any; timeParser?:any; //GeoChart Only geoJsonData:Object; // Projection, from https://github.com/mbostock/d3/wiki/Geo-Projections // For example, 'albersUsa' geoProjection:string; geoScale:number; } /** * dc.useRemoteData = true; * @type {boolean} * * -- this is required for server side data processing: this will also turn * off the filter re-settings when using remote data store; to avoid the * rapid backend firing * -- reference: if(!dc.useRemoteData)_chart.filter(null); */ dc.useRemoteData = true; /** * Singular class */ class Singular { public version:string = '1.0.1'; public items:any[] = [];//any chart type /** * @param useRemoteDataStore */ constructor() { } /** * render all the charts managed by me, CHARTS */ renderAll = () => { for (var chartName in this.items) { if (this.items.hasOwnProperty(chartName)) { this.items[chartName].render(); } } }; /** * redraw all the charts managed by me, CHARTS */ redrawAll = () => { for (var chartName in this.items) { if (this.items.hasOwnProperty(chartName)) { this.items[chartName].redraw(); } } }; /** * getAllFilters * @returns the string that has all the filters in CHARTS collection */ getAllFilters = ()=> { var currentFilters = [], chartName, chart, i; for (chartName in this.items) { if (this.items.hasOwnProperty(chartName)) { chart = this.items[chartName]; if (chart.filters && chart.filters() && chart.filters().length > 0) { var chartFilters = JSON.parse(JSON.stringify(chart.filters().slice(0)));//deep clone if (chart.xtickscale && chart.xtickscale > 0) { for (i = 0; i < chartFilters[0].length; i++) { chartFilters[0][i] = Math.floor(chartFilters[0][i] * chart.xtickscale); } } currentFilters[chartName] = chartFilters; } } } return currentFilters; }; /** *defaultFilterHandler * @param dimension * @param filter * @returns {*} */ public defaultFilterHandler = (dimension, filter) => { console.info("Chart.FilterHandler():filter=" + filter + " dimension = " + dimension); console.info(this.getAllFilters()); return filter;// return the actual filter value }; /** * updateDimension * @param conf * @returns {any} */ private updateDimension = function (conf) { if (conf && conf.field && !conf.itemId) { conf.itemId = conf.field + "-chart"; } try { if (conf && conf.itemId) { var elem = document.getElementById(conf.itemId); //console.info(elem.clientWidth, elem.clientHeight, elem.parentNode["clientHeight"]); conf.width = conf.width || (elem && (elem.clientWidth > 0 ) ? elem.clientWidth : elem.parentNode["clientWidth"]); conf.height = conf.height || (elem && (elem.clientHeight > 0) ? elem.clientHeight : elem.parentNode["clientHeight"]); } } catch (e) { console.info(e); } finally { conf.width = conf.width || 300; conf.height = conf.height || 300; } return conf; }; public getItemId = function (conf) { return conf.itemId ? conf.itemId : conf.field + '-chart'; }; public onResize = function (chart, itemId) { var getNewWidth = function (itemId) { var width = 300; try { var elem = document.getElementById(itemId); width = elem.offsetWidth || elem.parentNode["offsetWidth"]; // width=300; // console.info(elem.offsetWidth,elem.clientWidth, // elem.parentNode["offsetWidth"], elem.parentNode["clientWidth"]); } catch (e) { } finally { return width; } }; return (function () { return function () { chart.width(getNewWidth(itemId)); if (chart.hasOwnProperty('rescale')) { chart.rescale(); } chart.redraw().render(); } }()); }; /** * createBarChart * @param newconf * @returns {*} */ public createBarChart = function (newconf:ChartConfiguration):DC.BarChart { var me = this, conf = Singular.apply({}, newconf, { // xmin: 0, // xmax: 150, gap: 1, elasticX: false, numberFormat: d3.format(".0f"), xtickscale: 1, dimension: Singular.getDimensions([]), dimensionGroup: Singular.getGroupsFromData([]) }); conf = this.updateDimension(conf); var chart = dc.barChart('#' + me.getItemId(conf)); chart.xtickscale = conf.xtickscale; me.items[conf.field] = chart; chart.width(conf.width) .height(conf.height) .margins({ top: 10, right: 50, bottom: 30, left: 40 }) .dimension(conf.dimension) .group(conf.dimensionGroup) .elasticY(true) .elasticX(conf.elasticX); if (conf.ordinal && Array.isArray(conf.ordinal)) { chart.x(d3.scale.ordinal().domain(conf.ordinal)) .xUnits(dc.units.ordinal) .filterPrinter(function (filters) { return filters.join(','); }) } else { chart.x(d3.scale.linear().domain([conf.xmin, conf.xmax])) .gap(conf.gap) .filterPrinter(function (filters) { var filter = filters[0], s = ""; s += conf.numberFormat(filter[0] * conf.xtickscale) + " -> " + conf.numberFormat(filter[1] * conf.xtickscale) + " "; return s; }) .xAxis().ticks(5); chart.xAxis().tickFormat((v)=> { return v * conf.xtickscale + ''; }); } chart.renderHorizontalGridLines(true); //chart.centerBar(true) // .round(dc.round.floor) // .xUnits(dc.units.fp.precision(0.01)) chart.yAxis().ticks(5); chart.load = function (data) { chart.group(Singular.getGroupsFromData(data)).render(); return chart; }; return chart; }; /** * createBarChart * @param newconf * @returns {*} */ public createTimeSeriesBarChart = function (newconf:ChartConfiguration):DC.BarChart { var me = this, conf = Singular.apply({}, newconf, { xmin: new Date(2015, 2, 31), xmax: new Date(2015, 3, 10), gap: 1, elasticX: false, xUnits: d3.time.days, timeParser: d3.time.format("%d-%m-%Y %H:%M:%S").parse, dimension: Singular.getDimensions([]), dimensionGroup: Singular.getGroupsFromData([]) }); conf = this.updateDimension(conf); //console.info(conf); var chart = dc.barChart('#' + me.getItemId(conf)); me.items[conf.field] = chart; chart.width(conf.width) .height(conf.height) .margins({ top: 10, right: 50, bottom: 30, left: 40 }) .dimension(conf.dimension) .group(conf.dimensionGroup) .elasticY(true) .elasticX(conf.elasticX) //.centerBar(true)// .gap(conf.gap) .brushOn(true) .x(d3.time.scale().domain([conf.xmin, conf.xmax])) .xUnits(conf.xUnits) .renderHorizontalGridLines(true) .filterPrinter(function (filters) { var filter = filters[0], s = ""; s += filter[0] + " -> " + filter[1]; return s; }); chart.xAxis().ticks(5); chart.yAxis().ticks(5); chart.load = function (data) { data.forEach(function (d) { d.key = d3.time.day(conf.timeParser(d.key)); }); chart.group(Singular.getGroupsFromData(data)).render(); return chart; }; return chart; }; public createGeoChart = function(newconf:ChartConfiguration):DC.GeoChoroplethChart { var me = this, conf = Singular.apply({}, newconf, { dimension: Singular.getDimensions([]), dimensionGroup: Singular.getGroupsFromData([]) }); conf = this.updateDimension(conf); //console.info(conf); var chart = dc.geoChoroplethChart( conf.itemId ? '#' + conf.itemId : '#' + conf.field + "-chart"); me.items[conf.field] = chart; chart.width(conf.width) .height(conf.height) .dimension(conf.dimension) .group(conf.dimensionGroup) .colors(["#ccc", "#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]) .colorDomain([0, 200]) .title(function (d) { return d.key; }) .filterPrinter(function (filters) { return filters.join(', '); }) .overlayGeoJson(conf.geoJsonData.features, "state", function (d) { return d.properties.name; }); // .filterHandler((new Singular()).defaultFilterHandler) // .filterPrinter(function (filters) { // return filters.join(','); // }) // create a projection from d3 var projection = d3.geo[conf.geoProjection](); projection.scale(conf.geoScale) //these are sample numbers, will make the map about half the size .translate([conf.width / 2, conf.height / 2]); chart.projection(projection); // then, when it's time to make your chart... chart.load = function (data) { chart.group(Singular.getGroupsFromData(data)).render(); return chart; }; return chart; }; /** * createRowChart * -- function that create customized row chart * -- usage: * CHARTS.createRowChart({ * dimension:mwDimension, * dimensionGroup:mwDimension.group(), * field:"actvtyrow"}); * CHARTS.items[actvtyrow] * .width(200) * .height(120) * .dimension(CHARTS.helper.getDimensions([])) * .group(CHARTS.helper.getGroupsFromData([])) * .renderLabel(true) * .colors() * .colorDomain([]) * .label(function(d) { * return d.key;}).elasticX(true).xAxis().ticks(2); * * @param newconf * @returns {*} */ public createRowChart = function (newconf):DC.RowChart { var me = this, conf = Singular.apply({}, newconf, { field: "row", colors: d3.scale.category20c(), colorsdomain: [], //colors: ['red', 'green', 'blue', '#c6dbef', '#dadaeb'], //colorsdomain: ["active", "inactive", "unspecified", "inclonclusive"], dimension: Singular.getDimensions([]), dimensionGroup: Singular.getGroupsFromData([]) }); conf = this.updateDimension(conf); var chart = dc.rowChart('#' + me.getItemId(conf)); this.items[conf.field] = chart; chart.width(conf.width).height(conf.height).margins({ top: 10, right: 50, bottom: 30, left: 40 }).dimension(conf.dimension)// .group(conf.dimensionGroup)// .renderLabel(true).colors(conf.colors).label(function (d) { return d.key; }).elasticX(true).xAxis().ticks(2); chart.colors(conf.colors).colorDomain(conf.colorsdomain); chart.ordering(function (d) { return -d.value; }); chart.load = function (data) { chart.group(Singular.getGroupsFromData(data)).render(); return chart; }; return chart; }; /** * * function that create customized row chart * * Usage: * CHARTS.createRowChart({ * dimension:mwDimension, * dimensionGroup:mwDimension.group(), * field:'actvtyrow'}); */ public createPieChart = function (newconf):DC.PieChart { var me = this, conf = Singular.apply({}, newconf, { field: 'pie', width: 200, height: 120, innerRadius: 20, slicesCap: 5, //colors: ['red', 'green', 'blue', '#c6dbef', '#dadaeb'], //colorsdomain: ['active', 'inactive', 'unspecified', 'inclonclusive'], dimension: Singular.getDimensions([]), dimensionGroup: Singular.getGroupsFromData([]) }), chart = dc.pieChart('#' + me.getItemId(conf)); me.items[conf.field] = chart; chart.width(conf.width) .height(conf.height) .dimension(conf.dimension) .group(conf.dimensionGroup) .innerRadius(conf.innerRadius) .slicesCap(conf.slicesCap) .legend(dc.legend()); //chart.colors(conf.colors).colorDomain(conf.colorsdomain); chart.load = function (data) { chart.group(Singular.getGroupsFromData(data)).render(); return chart; }; return chart; }; /***************************************************** * static methods section ****************************************************/ /** * apply * - static method * - Copies all the properties of config to the specified object. * * @param {Object} * object The receiver of the properties * @param {Object} * config The source of the properties * @param {Object} * [defaults] A different object that will also be applied for * default values * @return {Object} returns obj */ static apply = function (o, n, q) { if (q) { this.apply(o, q); } if (o && n && typeof n === 'object') { var p; for (p in n) { if (n.hasOwnProperty(p)) { o[p] = n[p]; } } } return o; }; /** *getGroupsFromData * @param data * @returns {{top: (function(any): string|T[]|ArrayBuffer|Blob), all: (function(): *)}} */ static getGroupsFromData = function (data:any[]) { return { top: function (k) { return data.slice(0, k); }, all: function () { return data; } }; }; /** * getDimensions * @param data * @returns {{top: (function(any): any[]), filter: (function(any): void), filterFunction: (function(any): void)}} */ static getDimensions = function (data:any[]) { return { top: function (count) { return data.slice(0, count); }, filter: function (filter) { //console.log('dimention.filter():' + filter); }, filterFunction: function (filter) { //console.log('dimention.filterFunction():' + filter); } }; }; }