From: declerambaul Date: Tue, 26 Jun 2012 16:00:09 +0000 (+0200) Subject: D3 charttypes now render an individual metric, which allows to combine different... X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=33658c43c81f9dbad67c489d2701f5d29ba03a15;p=limn.git D3 charttypes now render an individual metric, which allows to combine different types in the same graph, e.g. having both bars and lines in a graph. --- diff --git a/lib/chart/type/d3/d3-bar-chart-type.co b/lib/chart/type/d3/d3-bar-chart-type.co index 3bcb596..0a257ff 100644 --- a/lib/chart/type/d3/d3-bar-chart-type.co +++ b/lib/chart/type/d3/d3-bar-chart-type.co @@ -40,6 +40,64 @@ class exports.BarChartType extends ChartType colors : dataset.getColors() labels : dataset.getLabels() options + + + renderChartType: (metric, svgEl ,xScale, yScale) -> + + X = (d, i) -> xScale d[0] + Y = (d, i) -> yScale d[1] + + + ### Render the line path + metricBars = root.metricBars = svgEl.append "g" + .attr "class", "metric bars "+metric.get 'label' + + data = d3.zip metric.getDateColumn(),metric.getData() + + ### Render Bars + barWidth = svgEl.attr('width')/data.length + barHeight = (d) -> svgEl.attr('height')-Y(d) + + metricBars.selectAll "bar" + .data data + .enter().append "rect" + .attr "class", (d, i) -> "metric bar #i" + .attr "x", X + .attr "y", Y + .attr "height", barHeight + .attr "width", -> barWidth + .attr "fill", metric.get 'color' + .attr "stroke", "white" + .style "opacity", "0.4" + .style "z-index", -10 + + + # adding event listeners + chT = this + metricBars.selectAll ".metric.bar" + .on "mouseover", (d, i) -> + + svgEl.append "text" + .attr "class", "mf" + .attr "dx", 50 + .attr "dy", 100 + .style "font-size", "0px" + .transition() + .duration(800) + .text "Uh boy, the target would be: "+chT.numberFormatter(d[1]).toString() + .style "font-size", "25px" + .on "mouseout", (d, i) -> + + svgEl.selectAll ".mf" + .transition() + .duration(300) + .text "BUMMER!!!" + .style "font-size", "0px" + .remove() + + + + svgEl renderChart: (data, viewport, options, lastChart) -> ### Starting with http://bost.ocks.org/mike/chart/ diff --git a/lib/chart/type/d3/d3-chart-type.co b/lib/chart/type/d3/d3-chart-type.co new file mode 100644 index 0000000..8f73c54 --- /dev/null +++ b/lib/chart/type/d3/d3-chart-type.co @@ -0,0 +1,121 @@ +d3 = require 'd3' +ColorBrewer = require 'colorbrewer' + +{ _, op, +} = require 'kraken/util' +{ ChartType, +} = require 'kraken/chart/chart-type' + +root = do -> this + + +class exports.D3ChartType extends ChartType + __bind__ : <[ determineSize ]> + SPEC_URL : '/schema/d3/d3-chart.json' + + # NOTE: ChartType.register() must come AFTER `typeName` declaration. + typeName : 'd3-chart' + ChartType.register this + + + /** + * Hash of role-names to the selector which, when applied to the view, + * returns the correct element. + * @type Object + */ + roles : + viewport : '.viewport' + legend : '.graph-legend' + + + # constructor: function D3ChartType + -> super ... + + + + getData: -> + @model.dataset.getColumns() + + + transform: -> + dataset = @model.dataset + options = @model.getOptions() import @determineSize() + options import do + colors : dataset.getColors() + labels : dataset.getLabels() + options + + + renderChart: (data, viewport, options, lastChart) -> + ### Starting with http://bost.ocks.org/mike/chart/ + + margin = {top: 20, right: 20, bottom: 20, left: 20} + width = 760 + height = 320 + xScale = d3.time.scale() + yScale = d3.scale.linear() + + dates = data[0] + cols = data.slice(1) + + # Calculate extents using all the data points (but not dates) + # allValues = d3.merge @model.dataset.getDataColumns() + allValues = d3.merge cols + + # Update the x-scale with the extents of the dates. + xScale + .domain d3.extent dates + .range [ 0, width - margin.left - margin.right ] + + # Update the y-scale with the extents of the data. + yScale + .domain d3.extent allValues + .range [ height - margin.top - margin.bottom, 0 ] + + # Select the svg element, if it exists. + svg = d3.select viewport.0 .selectAll "svg" + .data [cols] + + # ...Otherwise, create the skeletal chart. + enterFrame = svg.enter() + .append "svg" .append "g" + .attr "class", "frame" + + # Update chart dimensions. + svg .attr "width", width + .attr "height", height + + frame = svg.select "g.frame" + .attr "transform", "translate(#{margin.left},#{margin.top})" + .attr "width", width - margin.left - margin.right + .attr "height", height - margin.top - margin.bottom + + + # x-axis. + # TODO move axis to separate chart-type + enterFrame.append "g" + .attr "class", "x axis time" + + + xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0) + frame.select ".x.axis.time" + .attr "transform", "translate(0,#{yScale.range()[0]})" + .call xAxis + + for i,metric in @model.dataset.metrics.models + # metric defined charttype + chartType = metric.get "chartType" + # otherwise the graph defined charttype + # FOR NOW take line as default + chartType or= 'd3-line' # @model.get "chartType" + + + # create d3 charttype and render it + model = ChartType.create chartType + model.renderChartType metric, frame ,xScale, yScale + + + svg + + + diff --git a/lib/chart/type/d3/d3-line-chart-type.co b/lib/chart/type/d3/d3-line-chart-type.co index 6c67ead..e0974f7 100644 --- a/lib/chart/type/d3/d3-line-chart-type.co +++ b/lib/chart/type/d3/d3-line-chart-type.co @@ -28,11 +28,9 @@ class exports.LineChartType extends ChartType legend : '.graph-legend' - -> super ... - getData: -> @model.dataset.getColumns() @@ -45,6 +43,66 @@ class exports.LineChartType extends ChartType labels : dataset.getLabels() options + renderChartType: (metric, svgEl ,xScale, yScale) -> + + X = (d, i) -> xScale d[0] + Y = (d, i) -> yScale d[1] + line = d3.svg.line().x(X).y(Y) + + ### Render the line path + metricLine = root.metricLine = svgEl.append "g" + .attr "class", "g metric line "+metric.get 'label' + + data = d3.zip metric.getDateColumn(),metric.getData() + + metricLine.selectAll "path.line" + .data d3.zip data.slice(0,-1), data.slice(1) + .enter().append "path" + .attr "d", line + .attr "class", (d, i) -> "metric line segment #i" + .style "stroke", metric.get 'color' + + + ### Mouse Lens + lens = root.lens = svgEl.selectAll "g.lens" + .data [[]] + gLens = lens.enter().append "g" + .attr "class", "lens" + .style "z-index", 1e9 + gInner = gLens.append "g" + .attr "transform", "translate(1.5em,0)" + gInner.append "circle" + .attr "r", "1.5em" + # .style "opacity", "0.4" + # .style "fill", "white" + .style "fill", "rgba(255, 255, 255, 0.4)" + .style "stroke", "white" + .style "stroke-width", "3px" + gInner.append "text" + .attr "y", "0.5em" + .attr "text-anchor", "middle" + .style "fill", "black" + .style "font", "12px Helvetica" + .style "font-weight", "bold" + + # event listeners + chT = this + metricLine.selectAll ".line.segment" + .on "mouseover", (d, i) -> + + {r,g,b} = color = d3.rgb metric.get 'color' + lineX = (X(d[0])+X(d[1]))/2 + lineY = (Y(d[0])+Y(d[1]))/2 + + + lens = svgEl.select "g.lens" + .attr "transform", "translate(#lineX, #lineY)" + lens.select "circle" .style "fill", "rgba(#r, #g, #b, 0.4)" + lens.select "text" .text -> chT.numberFormatter(d[0][1]).toString() + + + svgEl + renderChart: (data, viewport, options, lastChart) -> ### Starting with http://bost.ocks.org/mike/chart/ diff --git a/lib/chart/type/d3/index.co b/lib/chart/type/d3/index.co index 06db1d2..3baf40d 100644 --- a/lib/chart/type/d3/index.co +++ b/lib/chart/type/d3/index.co @@ -1,5 +1,6 @@ +d3chart = require 'kraken/chart/type/d3/d3-chart-type' line = require 'kraken/chart/type/d3/d3-line-chart-type' -# geo = require 'kraken/chart/type/d3/d3-geo-chart-type' bar = require 'kraken/chart/type/d3/d3-bar-chart-type' +# geo = require 'kraken/chart/type/d3/d3-geo-chart-type' -exports import line import bar # import geo +exports import line import bar import d3chart # import geo diff --git a/lib/data/metric-model.co b/lib/data/metric-model.co index e270c82..605f1b8 100644 --- a/lib/data/metric-model.co +++ b/lib/data/metric-model.co @@ -43,6 +43,8 @@ Metric = exports.Metric = BaseModel.extend do # {{{ transforms : [] scale : 1.0 + + chartType : null diff --git a/www/d3-test.jade b/www/d3-test.jade new file mode 100644 index 0000000..968e48f --- /dev/null +++ b/www/d3-test.jade @@ -0,0 +1,39 @@ +extends layout + +block title + title Edit Graph | Limn + +append styles + mixin css('/vendor/bootstrap-colorpicker/css/colorpicker.css') + mixin css('/vendor/bootstrap-datepicker/css/datepicker.css') + mixin css('graph.css') + // new for SVG + mixin css('chart.css') + mixin css('data.css') + mixin css('isotope.css') + +block main-scripts + script + root = this + + // import all the things, ugly JS-style + d3 = require('d3') + Seq = require('seq') + Backbone = require('backbone') + util = require('kraken/util'); _ = util._; op = util.op + AppView = require('kraken/app').AppView + base = require('kraken/base'); BaseView = base.BaseView; BaseModel = base.BaseModel; BaseList = base.BaseList + ChartType = require('kraken/chart').ChartType + data = require('kraken/data'); DataSource = data.DataSource; DataSourceList = data.DataSourceList + _graph = require('kraken/graph'); Graph = _graph.Graph; GraphList = _graph.GraphList; GraphEditView = _graph.GraphEditView + //LineChartType = require('kraken/chart/type/d3/d3-line-chart-type') + + // run on DOM-ready + jQuery(function(){ + root.app = new AppView(function(){ + this.model = root.graph = new Graph({ id:'d3-test' }, { parse:true }) + this.view = root.view = new GraphEditView({ model:this.model }) + }); + }); + + diff --git a/www/modules.yaml b/www/modules.yaml index b74eba7..64c93c2 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -112,9 +112,10 @@ dev: - index - type: - d3: - - d3-line-chart-type - # - d3-geo-chart-type + - d3-chart-type + - d3-line-chart-type - d3-bar-chart-type + # - d3-geo-chart-type - index - dygraphs - index diff --git a/www/schema/d3/d3-chart.yaml b/www/schema/d3/d3-chart.yaml new file mode 100644 index 0000000..88a3564 --- /dev/null +++ b/www/schema/d3/d3-chart.yaml @@ -0,0 +1,9 @@ +- name: zoom.start + tags: + - interactivity + - zoom + type: Float + default: 1.0 + desc: Initial zoom-level, where 1.0 (100% zoom) shows the full map in the + frame (the default). +