Commit 9ea50408 authored by Kjetil Thuen's avatar Kjetil Thuen

Tweaked this and that

parent 63dbf9aa
......@@ -3,13 +3,18 @@ TimeBars
Introduction
------------
This project is a quick test to see how viable it would be to create animated
barcharts on top of the [Nesstar Rest API](https://prosjekt.nsd.uib.no/gitlab/nesstar/nesstar-rest-api/wikis/home)
This project is a quick and dirty implementation of the flash based timebars
used by Der Spiegel here: http://www.spiegel.de/flash/flash-24759.html
This implementation use data from the [Nesstar Rest API](https://prosjekt.nsd.uib.no/gitlab/nesstar/nesstar-rest-api/wikis/home), and D3 for the visualization.
Note that visualizations like these are very picky about their data.
Running
-------
To run this code you will need npm and grunt installed. Once those are in place, run the commands
To build and run this code you will need npm and grunt installed. Once those are
in place, run the commands
```
> npm install
......@@ -23,6 +28,5 @@ Deploying
---------
To deploy the demo, simply copy the buildt www folder to a web server. To use
the chart in you own page, copy www/js/ and www/css/ to your website, include
them in your page and have a look at look at the example index.html to see how
to insert the chart in your page.
`timebars.css` and `timebars.js` them in your page and have a look at look at
the example index.html to see how to insert the chart(s) in your page.
......@@ -2,7 +2,6 @@ timebars = require './timebars.coffee'
nesstarloader = require './nesstarloader.coffee'
timeBarAnim = undefined
currentFrame = 0
window.nesstartimebars = {}
window.nesstartimebars.main = (containerId, dataDesc) ->
......@@ -12,7 +11,7 @@ window.nesstartimebars.main = (containerId, dataDesc) ->
"Please wait"
nesstarloader.reloadNesstarData( (data) ->
timebars.setupChart containerId, data, dataDesc.display
currentFrame = timebars.nextFrame containerId, currentFrame
timebars.nextFrame containerId
, (errorStatus) ->
timebars.displayPoster containerId,
"fa fa-exclamation-triangle"
......@@ -28,7 +27,7 @@ window.nesstartimebars.main = (containerId, dataDesc) ->
.innerHTML = "<i class='fa fa-play'></i>"
else
timeBarAnim = setInterval ->
currentFrame = timebars.nextFrame containerId, currentFrame
timebars.nextFrame containerId
, timebars.speed()
document.getElementById(containerId + "playBtn")
.innerHTML = "<i class='fa fa-pause'></i>"
......
lodash = require 'lodash'
#Example requestDescription. Will fall back on this if none is passes to the
#reloadData function
requestDescription = () ->
{
host: "http://localhost"
port: 9001
service: "touch/hfk/restdata"
cube: "Fypr-2014-Bef_C1"
filter: {
measures: [ "Befolkning" ]
dimensions: [
{
"id": "tid"
},
{
"id": "alder"
"members": []
},
{
"id": "Kjnn",
"members": ["1","2"]
},
{
"id": "Framskrivingsalternativ"
"members": ["1"]
},
{
"id": "Region"
"members": ["12"]
}
]
}
}
fetchData = (url, callback, errFunc) ->
request = new XMLHttpRequest()
request.onerror = errFunc
......@@ -42,12 +8,11 @@ fetchData = (url, callback, errFunc) ->
request.send()
assembleDataRequest = (dataDesc) ->
datadesc = dataDesc or requestDescription()
datadesc.host + ":" +
datadesc.port + "/" +
datadesc.service + "/cube/" +
datadesc.cube + "/query?q=" +
JSON.stringify(datadesc.filter)
dataDesc.host + ":" +
dataDesc.port + "/" +
dataDesc.service + "/cube/" +
dataDesc.cube + "/query?q=" +
JSON.stringify(dataDesc.filter)
# Turns the over-verbose and rigid cell descriptions from the Nesstar REST cube
# JSON format into something a bit more manageable
......
......@@ -2,7 +2,6 @@ d3 = require 'd3-browserify'
lodash = require 'lodash'
dragDealer = require('dragdealer').Dragdealer
slider = {}
completeData = {}
chartWidth = (chartId) ->
......@@ -238,15 +237,15 @@ updateChart = (chartId, frameNum) ->
undefined
nextFrame = (chartId, frameNum) ->
nextFrame = (chartId) ->
dataLength = lodash.keys(completeData[chartId].yearData).length
if dataLength > 0
num = frameNum or 0
num = completeData[chartId].currentFrame or -1
num++
if num >= dataLength
num = 0
slider[chartId].setStep(num, 0, snap=true)
num
completeData[chartId].currentFrame = num
completeData[chartId].slider.setStep(num, 0, snap=true)
#Constructor.
#
......@@ -372,16 +371,18 @@ setupChart = (chartId, data, displayDetails) ->
.append "div"
.attr "class", "handle red-bar"
slider[chartId] = new dragDealer(chartId + "Slider", {
completeData[chartId].slider = new dragDealer(chartId + "Slider", {
steps: lodash.keys(data).length
animationCallback: ->
year = lodash.keys(completeData[chartId].yearData)[@getStep()[0] - 1]
frame = completeData[chartId].currentFrame = @getStep()[0]
d3.select "#" + chartId + " .dragdealer .handle"
.html year
callback: -> updateChart chartId, @getStep()[0]-1
.html lodash.keys(completeData[chartId].yearData)[frame]
callback: ->
completeData[chartId].currentFrame = @getStep()[0]
updateChart(chartId, completeData[chartId].currentFrame - 1)
})
nextFrame chartId, 0
nextFrame chartId
window.onresize = -> setupChart chartId, data, displayDetails
displayPoster = (chartId, icon, header, message) ->
......
@mixin rounded($radius: 0.5em) {
@mixin rounded($radius: 5px) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
......@@ -11,179 +10,161 @@
columns: $number;
}
#main {
margin: 1em;
}
#main svg {
@include rounded(1em);
}
.chartContainer {
@include rounded();
position: relative;
.controls {
height: 30px;
padding: 0px;
margin: 0px;
display: table;
.controls {
display: table;
border-spacing: 5px;
button {
width: 40px;
display: table-cell;
}
button {
display: table-cell;
}
.dragdealer {
@include rounded();
width: 100%;
display: table-cell;
background: none repeat scroll 0% 0% #E1E1E1;
height: 100%;
position: relative;
padding: 0px;
.handle {
.dragdealer {
@include rounded();
margin: 0px;
height: 100%;
padding-left: 1em;
padding-right: 1em;
background: none repeat scroll 0% 0% #C00;
color: #FFF;
font-size: 14px;
line-height: 30px;
text-align: center;
position: absolute;
top: 0px;
left: 0px;
cursor: pointer;
width: 100%;
display: table-cell;
background: #e1e1e1;
position: relative;
.handle {
@include rounded();
padding-left: 1em;
padding-right: 1em;
background: #c00;
color: white;
font-weight: bold;
line-height: 30px;
position: absolute;
cursor: pointer;
}
}
}
}
div.chartContainer {
position: relative;
}
.axisLabels {
path, line {
display: none;
}
.axisLabels {
path, line {
display: none;
text {
text-anchor: middle;
fill: black;
}
}
text {
text-anchor: middle;
fill: black;
}
}
.yAxisGrid {
path {
display: none;
}
.yAxisGrid {
path {
display: none;
line {
stroke: #f5f5f5;
stroke-width: 5;
}
}
line {
stroke: #f5f5f5;
stroke-width: 5;
}
}
.chart {
.valuepoint {
pointer-events: all;
}
.valuepoint text {
visibility: hidden;
text-anchor: middle;
dominant-baseline: central;
fill: #888;
}
.poster {
@include rounded();
position: relative;
margin: 5em;
background: rgba(0.2, 0.2, 0.2, 0.6);
padding: 2em;
color: #fff;
.icon {
position: absolute;
font-size: 50px;
top: 25px;
left: 30px;
clear: both;
g.hovered,
g.selected {
text {
visibility: visible;
}
rect {
stroke-width: 1px;
stroke: #c00;
}
}
}
.message {
div.metaData {
@include rounded();
position: absolute;
top: 60px;
left: 90px;
font-size: 14px;
clear: both;
left: 1em;
top: 1em;
background: rgba(0.2, 0.2, 0.2, 0.6);
padding: 0.5em;
color: #fff;
display: inline;
span.yearLabel {
font-size: 150%;
span.year {
font-weight: bold;
color: #c00;
text-shadow: 0 0 0.2em white, 0 0 0.2em white;
}
}
dl {
dt {
display: inline;
}
dt:after {
content: ": ";
}
dd {
display: inline;
padding-right: 2em;
}
}
}
.header {
.leftLabel {
font-size: 200%;
position: absolute;
top: 20px;
left: 90px;
font-size: 20px;
clear: both;
left: 0.5em;
bottom: 2em;
}
}
.chart {
.valuepoint {
pointer-events: all;
}
.valuepoint text {
visibility: hidden;
text-anchor: middle;
dominant-baseline: central;
fill: #888;
.rightLabel {
font-size: 200%;
position: absolute;
right: 1em;
bottom: 2em;
text-align: right;
}
g.hovered,
g.selected {
text {
visibility: visible;
.poster {
@include rounded();
position: relative;
margin: 5em;
background: rgba(0.2, 0.2, 0.2, 0.6);
padding: 2em;
color: #fff;
.icon {
position: absolute;
font-size: 50px;
top: 25px;
left: 30px;
clear: both;
}
rect {
stroke-width: 1px;
stroke: #f44;
.message {
position: absolute;
top: 60px;
left: 90px;
font-size: 14px;
clear: both;
}
}
}
div.metaData {
@include rounded();
position: absolute;
left: 1em;
top: 1em;
background: rgba(0.2, 0.2, 0.2, 0.6);
padding: 0.5em;
color: #fff;
display: inline;
span.yearLabel {
font-size: 150%;
span.year {
font-weight: bold;
color: #f44;
text-shadow: 0 0 0.2em white, 0 0 0.2em white;
}
}
dl {
dt {
display: inline;
}
dt:after {
content: ": ";
}
dd {
display: inline;
padding-right: 2em;
.header {
position: absolute;
top: 20px;
left: 90px;
font-size: 20px;
clear: both;
}
}
}
div.leftLabel {
font-size: 200%;
position: absolute;
left: 0.5em;
bottom: 2em;
}
div.rightLabel {
font-size: 200%;
position: absolute;
right: 1em;
bottom: 2em;
text-align: right;
}
......@@ -10,11 +10,13 @@
</head>
<body>
<section id="main">
<!-- A chartContainer must have the class "chartContainer" and a unique id -->
<div class="chartContainer" id="content"></div>
</section>
</section>
<script>
//Values for hfk Nesstar REST api proxied through Grunt
dataDesc = {
//An object describing how and from where to fetch the numbers. The
//"display" part is used for tweaking the presentation
befolkningBergen = {
host: "http://nesstar-dev.nsd.uib.no",
port: 80,
service: "touch/hfk/restdata",
......@@ -56,10 +58,9 @@
}
};
/* #content is the id of the container that should contain the chart. The
dataDesc object is a copy of the hardcoded defaults. Omitting the
dataDesc parameter is equivalent to passing the object above. */
window.nesstartimebars.main("content", dataDesc);
/* Initializing the timebars component in the #content div, using the
befolkningBergen object */
window.nesstartimebars.main("content", befolkningBergen);
</script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment