D3 charttypes now render an individual metric, which allows to combine different...
authordeclerambaul <fabian.kaelin@gmail.com>
Tue, 26 Jun 2012 16:00:09 +0000 (18:00 +0200)
committerdeclerambaul <fabian.kaelin@gmail.com>
Tue, 26 Jun 2012 16:00:09 +0000 (18:00 +0200)
data/graphs/d3-test.json [new file with mode: 0644]
lib/chart/type/d3/d3-bar-chart-type.co
lib/chart/type/d3/d3-chart-type.co [new file with mode: 0644]
lib/chart/type/d3/d3-line-chart-type.co
lib/chart/type/d3/index.co
lib/data/metric-model.co
www/d3-test.jade [new file with mode: 0644]
www/modules.yaml
www/schema/d3/d3-chart.yaml [new file with mode: 0644]

diff --git a/data/graphs/d3-test.json b/data/graphs/d3-test.json
new file mode 100644 (file)
index 0000000..eef06a1
--- /dev/null
@@ -0,0 +1,146 @@
+{
+    "options": {
+        "animatedZooms": true,
+        "avoidMinZero": false,
+        "axis": null,
+        "axisLabelColor": "#666666",
+        "axisLabelFontSize": 11,
+        "axisLabelFormatter": null,
+        "axisLabelWidth": 50,
+        "axisLineColor": "#AAAAAA",
+        "axisLineWidth": 0.3,
+        "axisTickSize": 3,
+        "colorSaturation": 1,
+        "colorValue": 0.5,
+        "colors": ["#FF0097", "#EF8158", "#83BB32", "#182B53", "#4596FF", "#553DC9", "#AD3238", "#00FFBC", "#F1D950"],
+        "connectSeparatedPoints": false,
+        "customBars": false,
+        "dateWindow": null,
+        "delimiter": ",",
+        "digitsAfterDecimal": 2,
+        "displayAnnotations": false,
+        "drawPoints": true,
+        "drawXAxis": true,
+        "drawXGrid": true,
+        "drawYAxis": true,
+        "drawYGrid": true,
+        "errorBars": false,
+        "file": null,
+        "fillAlpha": 0.15,
+        "fillGraph": false,
+        "fractions": false,
+        "gridLineColor": "#D8D8D8",
+        "gridLineWidth": 0.3,
+        "hideOverlayOnMouseOut": false,
+        "highlightCircleSize": 4,
+        "includeZero": false,
+        "interactionModel": null,
+        "isZoomedIgnoreProgrammaticZoom": false,
+        "labels": null,
+        "labelsDiv": null,
+        "labelsDivStyles": null,
+        "labelsDivWidth": 250,
+        "labelsKMB": true,
+        "labelsKMG2": false,
+        "labelsSeparateLines": true,
+        "labelsShowZeroValues": true,
+        "legend": "always",
+        "logscale": true,
+        "maxNumberWidth": 30,
+        "panEdgeFraction": null,
+        "pixelsPerLabel": null,
+        "pixelsPerXLabel": null,
+        "pixelsPerYLabel": null,
+        "pointSize": 1,
+        "rangeSelectorHeight": 40,
+        "rangeSelectorPlotFillColor": "#A7B1C4",
+        "rangeSelectorPlotStrokeColor": "#808FAB",
+        "rightGap": 20,
+        "rollPeriod": 1,
+        "showLabelsOnHighlight": true,
+        "showRangeSelector": false,
+        "showRoller": false,
+        "sigFigs": null,
+        "sigma": 2,
+        "stackedGraph": false,
+        "stepPlot": false,
+        "strokePattern": null,
+        "strokeWidth": 4,
+        "ticker": null,
+        "title": null,
+        "titleHeight": 18,
+        "valueFormatter": null,
+        "valueRange": null,
+        "visibility": null,
+        "wilsonInterval": true,
+        "xAxisHeight": null,
+        "xAxisLabelFormatter": null,
+        "xAxisLabelWidth": 55,
+        "xLabelHeight": 18,
+        "xValueFormatter": null,
+        "xValueParser": null,
+        "xlabel": null,
+        "y2label": null,
+        "yAxisLabelFormatter": null,
+        "yAxisLabelWidth": 50,
+        "yLabelWidth": 18,
+        "yValueFormatter": null,
+        "ylabel": null
+    },
+    "slug": "d3-test",
+    "name": "",
+    "desc": "",
+    "notes": "",
+    "width": "auto",
+    "height": 320,
+    "parents": ["root"],
+    "data": {
+        "palette": null,
+        "lines": [],
+        "metrics": [{
+            "index": 0,
+            "label": "Total",
+            "type": "int",
+            "timespan": {
+                "start": null,
+                "end": null,
+                "step": null
+            },
+            "disabled": false,
+            "source_id": "rc_page_requests_mobile_target",
+            "source_col": 1,
+            "color": "#00b2ff",
+            "visible": true,
+            "format_value": null,
+            "format_axis": null,
+            "transforms": [],
+            "scale": 1
+        }, {
+            "index": 1,
+            "label": "Target",
+            "type": "int",
+            "timespan": {
+                "start": null,
+                "end": null,
+                "step": null
+            },
+            "disabled": false,
+            "source_id": "rc_page_requests_mobile_target",
+            "source_col": 2,
+            "color": "rgb(213,62,79)",
+            "visible": true,
+            "format_value": null,
+            "format_axis": null,
+            "transforms": [],
+            "scale": 1,
+            "chartType": "d3-bar"
+        }]
+    },
+    "callout": {
+        "enabled": true,
+        "metric_idx": 0,
+        "label": ""
+    },
+    "chartType": "d3-chart",
+    "id": "d3-test"
+}
index 3bcb596..0a257ff 100644 (file)
@@ -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 (file)
index 0000000..8f73c54
--- /dev/null
@@ -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
+    
+    
+    
index 6c67ead..e0974f7 100644 (file)
@@ -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/
index 06db1d2..3baf40d 100644 (file)
@@ -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 
index e270c82..605f1b8 100644 (file)
@@ -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 (file)
index 0000000..968e48f
--- /dev/null
@@ -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 })
+            });
+        });
+    
+    
index b74eba7..64c93c2 100644 (file)
@@ -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 (file)
index 0000000..88a3564
--- /dev/null
@@ -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).
+