From: David Schoonover Date: Wed, 20 Jun 2012 08:15:25 +0000 (-0700) Subject: First pass at a d3 line chart. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=17eaea5d7c6916e183b76d879254f1cc287ccf7e;p=limn.git First pass at a d3 line chart. --- diff --git a/lib/base/base-view.co b/lib/base/base-view.co index 21c1cbc..18646a5 100644 --- a/lib/base/base-view.co +++ b/lib/base/base-view.co @@ -4,6 +4,8 @@ Backbone = require 'backbone' } = require 'kraken/util' { BaseBackboneMixin, mixinBase, } = require 'kraken/base/base-mixin' +{ BaseModel, +} = require 'kraken/base/base-mixin' { DataBinding, } = require 'kraken/base/data-binding' diff --git a/lib/chart/chart-type.co b/lib/chart/chart-type.co index ca74132..b10a154 100644 --- a/lib/chart/chart-type.co +++ b/lib/chart/chart-type.co @@ -395,7 +395,7 @@ class exports.ChartType extends ReadyEmitter * * @returns {Object} The derived data. */ - transform: -> + transform: (model, view) -> @model.getOptions() diff --git a/lib/chart/type/d3/d3-bar-chart-type.co b/lib/chart/type/d3/d3-bar-chart-type.co index e69de29..e1fc007 100644 --- a/lib/chart/type/d3/d3-bar-chart-type.co +++ b/lib/chart/type/d3/d3-bar-chart-type.co @@ -0,0 +1,10 @@ +d3 = require 'd3' + +{ _, op, +} = require 'kraken/util' +{ ChartType, +} = require 'kraken/chart' + + + +exports.foo = 1 diff --git a/lib/chart/type/d3/d3-geo-chart-type.co b/lib/chart/type/d3/d3-geo-chart-type.co index 4709aa0..26d5ad9 100644 --- a/lib/chart/type/d3/d3-geo-chart-type.co +++ b/lib/chart/type/d3/d3-geo-chart-type.co @@ -5,6 +5,9 @@ ColorBrewer = require 'colorbrewer' { ChartType, } = require 'kraken/chart' + + + class GeoWorldChartType extends ChartType __bind__ : <[ dygNumberFormatter dygNumberFormatterHTML ]> SPEC_URL : '/schema/d3/d3-geo-world.json' @@ -72,6 +75,11 @@ class GeoWorldChartType extends ChartType path = d3.geo.path() .projection projection + # path objects + feature = map.selectAll ".feature" + infobox = d3.select '.infobox' + + move = -> projection .translate d3.event.translate @@ -95,9 +103,6 @@ class GeoWorldChartType extends ChartType .attr "transform", "translate(0,0)" .call zoom - # path objects - feature := map.selectAll ".feature" - # rectangle map.append "svg:rect" .attr "class", "frame" @@ -106,8 +111,6 @@ class GeoWorldChartType extends ChartType ### infobox - infobox := d3.select '#infobox' - infobox.select '#ball' .append "svg:svg" .attr "width", "100%" @@ -157,7 +160,7 @@ class GeoWorldChartType extends ChartType - +data = null main = -> jQuery.ajax do url : "/data/geo/data/en_geo_editors.json" diff --git a/lib/chart/type/d3/d3-line-chart-type.co b/lib/chart/type/d3/d3-line-chart-type.co index e69de29..ccf27a1 100644 --- a/lib/chart/type/d3/d3-line-chart-type.co +++ b/lib/chart/type/d3/d3-line-chart-type.co @@ -0,0 +1,121 @@ +d3 = require 'd3' +ColorBrewer = require 'colorbrewer' + +{ _, op, +} = require 'kraken/util' +{ ChartType, +} = require 'kraken/chart' + + +class LineChartType extends ChartType + __bind__ : <[ determineSize ]> + SPEC_URL : '/schema/d3/d3-line.json' + + # NOTE: ChartType.register() must come AFTER `typeName` declaration. + typeName : 'd3-line' + 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' + + + + -> super ... + + transform: -> + dataset = @model.dataset + options = @model.getOptions() import @determineSize() + options import do + colors : dataset.getColors() + options + + + renderChart: (rawData, viewport, options, lastChart) -> + viewport.empty() + + ### Starting with http://bost.ocks.org/mike/chart/ + + + # formatDate = d3.time.format("%b %Y") + # chart = timeSeriesChart() + # .x (d) -> formatDate.parse(d.date) + # .y (d) -> +d.price + # d3.csv("sp500.csv", (data) -> + # d3.select("#example") + # .datum(data) + # .call(chart) + + margin = {top: 20, right: 20, bottom: 20, left: 20} + width = 760 + height = 320 + xValue = (d) -> d[0] + yValue = (d) -> d[1] + xScale = d3.time.scale() + yScale = d3.scale.linear() + xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0) + X = (d) -> xScale(d[0]) + Y = (d) -> yScale(d[1]) + line = d3.svg.line().x(X).y(Y) + + + chart = (selection) -> + selection.each (data) -> + + # Convert data to standard representation greedily + # this is needed for nondeterministic accessors. + data = data.map (d, i) -> + [ xValue.call(data, d, i), yValue.call(data, d, i) ] + + + # Update the x-scale. + xScale + .domain d3.extent data, xValue + .range [ 0, width - margin.left - margin.right ] + + # Update the y-scale. + yScale + .domain [ 0, d3.max data, yValue ] + .range [ height - margin.top - margin.bottom, 0 ] + + # Select the svg element, if it exists. + svg = d3.select(this).selectAll("svg").data([data]) + + # Otherwise, create the skeletal chart. + gEnter = svg.enter().append("svg").append("g") + gEnter.append("path") + .attr "class", "line" + .style "stroke", (d, i) -> options.colors[i] + gEnter.append("g").attr("class", "x axis") + + # Update the outer dimensions. + svg .attr("width", width) + .attr("height", height) + + # Update the inner dimensions. + g = svg.select("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") + + # Update the line path. + g.select(".line") + .attr("d", line) + + # Update the x-axis. + g.select(".x.axis") + .attr("transform", "translate(0," + yScale.range()[0] + ")") + .call(xAxis) + + d3.select viewport.0 + .datum rawData + .call chart + + + + +module.exports = exports = LineChartType \ No newline at end of file diff --git a/lib/dashboard/dashboard-model.co b/lib/dashboard/dashboard-model.co index 4ae98dc..16e1dd8 100644 --- a/lib/dashboard/dashboard-model.co +++ b/lib/dashboard/dashboard-model.co @@ -1,8 +1,8 @@ { _, op, } = require 'kraken/util' -{ BaseView, +{ BaseModel, } = require 'kraken/base' -{ Graph, +{ Graph, GraphList, } = require 'kraken/graph/graph-model' @@ -10,10 +10,14 @@ * @class */ Dashboard = exports.Dashboard = BaseModel.extend do # {{{ - urlRoot : '/dashboards' + urlRoot : '/dashboards' + graphs : null # GraphList + byTab : null # GraphList[] constructor: function Dashboard + @graphs = new GraphList + @byTab = [] BaseModel ... initialize: -> @@ -22,7 +26,31 @@ Dashboard = exports.Dashboard = BaseModel.extend do # {{{ defaults: -> name : null - graphs : [] + tabs : [ { name:"Main", graph_ids:[] } ] + + + + + addTab: (tabName) -> + ... + + + /** + * Look up a tab. + * + * @param {String|Number} tab Tab name or index. + * @returns {Tab} Tab object. + */ + getTab: (tab) -> + tabs = @get 'tabs' + return tabs[tab] if typeof tab is 'number' + _.find tabs, -> it.name is tab + + addGraph: (graph, tabName) -> + ... + + getGraphs: (tab) -> + ... # }}} diff --git a/lib/data/metric-model.co b/lib/data/metric-model.co index 3077fd6..e270c82 100644 --- a/lib/data/metric-model.co +++ b/lib/data/metric-model.co @@ -65,10 +65,10 @@ Metric = exports.Metric = BaseModel.extend do # {{{ getDateColumn: -> - @source.getDateColumn() + @source?.getDateColumn() getData: -> - @source.getColumn @get 'source_col' + @source?.getColumn @get 'source_col' getLabel: -> @get('label') or @getPlaceholderLabel() diff --git a/lib/main-edit.co b/lib/main-edit.co index ae9b194..74040d5 100644 --- a/lib/main-edit.co +++ b/lib/main-edit.co @@ -1,5 +1,3 @@ -{EventEmitter} = require 'events' - Seq = require 'seq' Backbone = require 'backbone' diff --git a/lib/server/server.co b/lib/server/server.co index bb01baa..1f8788a 100755 --- a/lib/server/server.co +++ b/lib/server/server.co @@ -118,7 +118,7 @@ app.configure -> log_level : LOG_LEVEL app.use require('browserify') do mount : '/vendor/browserify.js' - require : <[ seq events ]> + require : <[ seq d3 events ]> cache : false # cache : "#CWD/.cache/browserify/cache.json" diff --git a/lib/template/graph/graph-display.jade b/lib/template/graph/graph-display.jade index f15e2b6..24de1a0 100644 --- a/lib/template/graph/graph-display.jade +++ b/lib/template/graph/graph-display.jade @@ -12,7 +12,7 @@ section.graph-display.graph(id=graph_id) span.dates(data-bind='callout.month.dates') #{callout.month.dates} span.value(data-bind='callout.month.value') #{callout.month.value} h2 - a.graph-name(href="#{model.toLink()}", data-bind='name') #{name} + a.graph-name(href="#{model.toLink()}", data-bind='name', name="#{graph_id}") #{name} .graph-viewport-row.row-fluid: .inner .viewport diff --git a/www/css/chart.styl b/www/css/chart.styl new file mode 100644 index 0000000..b9b7239 --- /dev/null +++ b/www/css/chart.styl @@ -0,0 +1,11 @@ +// Styling for SVG charts + +.viewport svg + + .line + fill none + stroke #000 + stroke-width 1.5px + + + diff --git a/www/d3-test.jade b/www/d3-test.jade new file mode 100644 index 0000000..39fbc87 --- /dev/null +++ b/www/d3-test.jade @@ -0,0 +1,23 @@ +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') + mixin css('chart.css') + mixin css('data.css') + mixin css('isotope.css') + +block main-scripts + script + d3 = require('d3') + LineChartType = require('kraken/chart/type/d3/d3-line-chart-type') + jQuery(function(){ + setTimeout(function(){ + ct = view.chartType = graph.chartType = new LineChartType(graph, view); + }, 100); + }); + script(src="/js/kraken/main-edit.js?"+version) diff --git a/www/modules.yaml b/www/modules.yaml index c11206c..4ee875a 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -40,7 +40,7 @@ dev: - moment.mod.min - dygraph - dygraph-extra - - d3 + # - d3 - suffix: .mod.js paths: @@ -111,6 +111,11 @@ dev: - chart-option-view - index - type: + - d3: + - d3-line-chart-type + - d3-geo-chart-type + - d3-bar-chart-type + - index - dygraphs - index - chart-type diff --git a/www/schema/d3/d3-line.yaml b/www/schema/d3/d3-line.yaml new file mode 100644 index 0000000..88a3564 --- /dev/null +++ b/www/schema/d3/d3-line.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). +