D3 Axis transition - transition

The visualization is a bar chart in which the range of the bar heights is great (several orders of magnitude). I have built a transition triggered by a click that increases the heights of all the bars by a multiplicative amount. I would also like the y-axis ticks and labels to transition to the new scale of the bars. I have altered the domain of the y-axis scale. The bar height transition works fine. The axis transition does nothing.I cannot seem to get the axis transition correct. Can anyone help?
The code is a simple example of what I am trying to do - data is fixed.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="../static/js/d3.js"></script>
<style type="text/css">
.axis, .axis text {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tick text {
font-family: sans-serif;
fill: black;
}
</style>
</head>
<body>
<script type="text/javascript">
var xSize = 900,
ySize = 500;
var data = [10000, 3000, 100, 40, 10, 5, 1];
var margin = {top:40, right: 40, bottom:60, left: 60};
var width = xSize - margin.left - margin.right;
var height = ySize - margin.top - margin.bottom;
var barwidth = width / data.length;
var xscale = d3.scale.linear().domain([0, data.length]).range([0, width]);
var maxData = d3.max(data);
var yscale = d3.scale.linear().domain([0, maxData]).range([height, 0]);
var xAxis = d3.svg.axis().scale(xscale).orient("bottom");
var yAxis = d3.svg.axis().scale(yscale).orient("left");
var fieldView = d3.select("body")
.append("svg")
.attr("width", xSize)
.attr("height", ySize)
.on("click", update)
;
var chart = fieldView
.append("g")
.attr("id", "clip")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
;
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i) {return xscale(i);})
.attr("y", function(d) { return yscale(d);})
.attr("height", function(d) { return height - yscale(d);})
.attr("width", barwidth)
.attr("fill", "steelblue")
.attr("clip-path", "url(#clip)")
;
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
;
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
;
var transitionScale = 1;
var transitionMultiplier = 5;
var transitionYscale = d3.scale.linear().domain([0, maxData])
.range([height, 0]);
function update() {
// new multiplier for data
transitionScale *= transitionMultiplier;
// update to y axis domain
yscale.domain([0, maxData / transitionScale]);
// transition the bars
bar
.transition()
.attr("y", function(d) {return yscale(d);})
.attr("height", function(d) {return height - yscale(d);})
.duration(2000)
.ease("linear")
;
// transition the y-axis
chart
.selectAll(".y axis")
.transition()
.call(yAxis)
;
}
</script>
</body>
</html>

Changing the axis transition selection from:
chart.selectAll(".y axis")
to:
chart.selectAll(".y.axis")
causes the axis to transition correctly. I don't know why, however.

Related

Why does mic.getLevel() not go to 0 again after getAudioContext().suspend() is called?

Making a voice recorder visualizer and I'm just about finished but there's one thing, After I stop the recording, the values in mic.getLevel() do not go back to 0 but instead it seems like the last value that was recorded in mic.getLeve() is stored permanently and added to the height of my ellipse so the ellipse would then have a height of some value rather than 0 which it started with, is there anyway to fix this?
var recordAudio;
function setup() {
createCanvas(windowWidth, windowHeight);
recordAudio = new AudioFile()
}
function draw() {
background(0);
recordAudio.draw();
recordAudio.setup();
recordAudio.drawBorder();
recordAudio.drawNode();
}
function AudioFile() {
this.nodes = [];
var speed = 2;
var endBorder;
var mic = new p5.AudioIn();
var micLevel;
var level;
var recorder = new p5.SoundRecorder();
var soundFile = new p5.SoundFile();
var button = createButton('Start Recording');
var state = 0;
this.draw = function() {
background(0);
level = mic.getLevel();
micLevel = floor(map(level, 0, 0.6545, 0, 50));
}
this.drawNode = function() {
if (frameCount % 5 == 0) {
this.addNode()
}
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i]
for (var j = 0; j < node.length; j++) {
fill(255);
node[j].x -= speed;
ellipse(node[j].x, node[j].y, node[j].width, node[j].height)
}
if (node[0].x < endBorder) {
this.nodes.splice(i, 1);
}
}
}
this.drawBorder = function() {
var x = windowWidth / 9;
var y = windowHeight / 10;
var width = (windowWidth / 9) * 7;
var height = windowHeight - y * 2;
stroke(255);
strokeWeight(2);
noFill();
rect(x, y, width, height);
}
this.addNode = function() {
this.nodes.push(
[{
x: ((windowWidth / 9) * 8) - 10,
y: windowHeight / 2,
width: 5,
height: 5 * micLevel
}])
}
this.setup = function() {
endBorder = windowWidth / 9 + 5;
mic.start();
recorder.setInput(mic);
button.position(windowWidth / 9, windowHeight / 10);
button.style('font-size', '18px');
button.mousePressed(this.recording)
}
this.recording = function() {
if (state === 0 && mic.enabled) {
button.html("Stop Recording");
getAudioContext().resume()
recorder.record(soundFile);
state++
} else if (state === 1) {
button.html("Start Recording");
getAudioContext().suspend();
recorder.stop();
state++;
} else if (state === 2) {
save(soundFile, 'Sound.wav');
state = 0;
}
}
}
<!DOCTYPE html>
<html>
<head>
<link href="style.css" rel="stylesheet">
<script src="p5.min.js"></script>
<script src="p5.dom.js"></script>
<script src="p5.sound.js"></script>
<script src="sketch.js"></script>
<!--<link rel="stylesheet" type="text/css" href="style.css">-->
<style>
body {
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="Button">
</div>
</body>
</html>
I ended up making a global variable called listening and used that in an if statement in draw to set the level to either mic.getLevel() or 0 based on if listening is true or false.

Add text on the circle created by the leaflet L.circle() function

I use the following function to create a series of circles around a clicked point on a leaflet map. I would like to label each circle by placing the distance (r or radius) on the circle line itself. But I can't find any option or function to do this. Can someone tell me how?
function circleKoords(e) {
var myJSON = JSON.stringify(e.latlng);
//alert("myJSON "+myJSON);
// myJSON {"lat":39.257778150283364,"lng":-94.41925048828126}
// myJSON {"lat":39.39587712612034,"lng":-93.54583740234376}
var LatLng = e.latlng;
var lat = e.latlng["lat"]; //alert(lat); // 39.172658670429946
var lng = e.latlng["lng"]; //lert(lng);
var i;
var r = 1609.34; // in meters = 1 mile, 4,828.03 meters in 3 miles
//https://jsfiddle.net/3u09g2mf/2/
//https://gis.stackexchange.com/questions/240169/leaflet-onclick-add-remove-circles
var group1 = L.featureGroup(); // Allows us to group the circles for easy removal
var circleOptions = {
color: 'blue',
fillColor: '#69e',
fillOpacity: 0.1
}
var milesbetween = 0; var numberofrings = 0;
var milesbetween = prompt("How many miles between circles?");
if (milesbetween <= 0) {milesbetween = 1;}
var numberofrings = prompt("How many circles do you want to see?");
if (numberofrings <= 0) {numberofrings = 5;}
// The actual circles are created here at the var Cname =
for (i=0 ; i < numberofrings; i++ ) {
var Cname = 'circle'+i; //alert(Cname); // circle0, circle1, circle2, etc.
r = (r * i) + r; r=r*milesbetween;
//alert(lat+", "+lng); // lat and lng of clicked point.
//alert('r= '+r); // r= 1609.34, r= 3218.68, r= 4828.0199999999995, etc. up to the number of rings
var Cname = L.circle([lat, lng], r, circleOptions);
Cname.addTo(group1);
// r = 1609.34; // reset r so r calculation above works for each 1 mile step
map.addLayer(group1);
r = 1609.34;
}
alert("All "+numberofrings+" rings are "+milesbetween+" mile apart.");
// This part allows us to delete the circles by simply clicking anywhere in the circles.
group1.on('click', function() {
if(map.hasLayer(group1)) {map.removeLayer(group1);}
//else {map.addLayer(group1);}
});
}
Can you try putting this after "var Cname = L.circle([lat, lng], r, circleOptions);"? This attempts to add a label to the same coordinates with the text "radius" followed by the computed radius value.
var label = L.marker([lat, lng], {
icon: L.divIcon({
className: 'label',
html: `radius ${r}`,
iconSize: [0, 0]
})
});
label.addTo(group1);
Add the L.GeometryUtil library:
<script src="https://cdn.jsdelivr.net/npm/leaflet-geometryutil#0.9.1/src/leaflet.geometryutil.min.js"></script>
Update your circle loop to:
// The actual circles are created here at the var Cname =
for (i=0 ; i < numberofrings; i++ ) {
var Cname = 'circle'+i; //alert(Cname); // circle0, circle1, circle2, etc.
r = (r * i) + r; r=r*milesbetween;
//alert(lat+", "+lng); // lat and lng of clicked point.
//alert('r= '+r); // r= 1609.34, r= 3218.68, r= 4828.0199999999995, etc. up to the number of rings
var Cname = L.circle([lat, lng], r, circleOptions);
Cname.addTo(group1);
mymap.addLayer(group1);
//180° from top
var p_c = L.GeometryUtil.destination(L.latLng([lat, lng]),180,r);
var icon = L.divIcon({ className: 'dist-marker', html: r.toFixed(0)+';iles', iconSize: [null, 16] });
var marker = L.marker(p_c, { title: r.toFixed(0)+'Miles', icon: icon});
marker.addTo(mymap);
// r = 1609.34; // reset r so r calculation above works for each 1 mile step
r = 1609.34;
}
CSS:
.dist-marker {
margin-left: -8px !important;
font-size: 9px;
border: 1px solid #777;
border-radius: 10px;
text-align: center;
color: #000;
background: #fff;
padding: 0 3px;
}
https://jsfiddle.net/falkedesign/3aukgm7t/

Bing Maps containsLocation function

I have a site that uses both Bing and Google maps. Each function has a Bing and Google version. I am having trouble duplicating the google.maps.geometry.poly.containsLocation function in Bing maps. Is there such a thing?
Basically I build a polygon and am looking to determine if a pushpin is located inside the polygon on the map.
Bing Maps V8 has a Spatial Math module which can do this calculation for you easily using the intersects function:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
</head>
<body>
<div id='myMap' style='width: 100vw; height: 100vh;'></div>
<script type='text/javascript'>
function load() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
credentials: 'YOUR BING MAPS KEY'
});
//Create a polygon and location for testing.
var center = map.getCenter();
var polygon = new Microsoft.Maps.Polygon([new Microsoft.Maps.Location(center.latitude - 0.05, center.longitude - 0.05),
new Microsoft.Maps.Location(center.latitude + 0.01, center.longitude - 0.05),
new Microsoft.Maps.Location(center.latitude + 0.01, center.longitude + 0.05)], { fillColor: 'yellow', strokeColor: 'orange',
strokeThickness: 5, strokeDashArray: [1, 2, 5, 10] });
map.entities.push(polygon);
var location = new Microsoft.Maps.Location(center.latitude, center.longitude);
//Load the Spatial Math module
Microsoft.Maps.loadModule('Microsoft.Maps.SpatialMath', function () {
//Check to see if the shapes intersect.
var intersects = Microsoft.Maps.SpatialMath.Geometry.intersects(location, polygon);
if(intersects){
alert("The location is inside in the polygon");
} else {
alert("The location is NOT inside in the polygon");
}
});
}
</script>
<script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?callback=load' async defer></script>
</body>
</html>
You can add your own method to do so using the extensibility of Bing Maps AJAX control. You can put that extension method on the Microsoft.Maps.Location class.
Microsoft.Maps.Location.prototype.IsInPolygon=function(polygon)
{
var isInside = false;
var j = 0;
var x = this.longitude;
var y = this.latitude;
var paths = polygon.getLocations();
for (var i = 0; i < paths.length ; i++) {
j++;
if (j == paths.length) { j = 0; }
if (((paths[i].latitude < y) && (paths[j].latitude >= y)) || ((paths[j].latitude < y) && (paths[i].latitude >= y))) {
if (paths[i].longitude + (y - paths[i].latitude) / (paths[j].latitude - paths[i].latitude) * (paths[j].longitude - paths[i].longitude) < x) {
isInside = !isInside
}
}
}
return isInside;
};
Here is a working example with Bing Maps V8:
<!DOCTYPE html>
<html>
<head>
<title>Bing Maps - V8 - Polygon test</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
</head>
<body>
<div id='myMap' style='width: 100vw; height: 100vh;'></div>
<script type='text/javascript'>
function load() {
Microsoft.Maps.Location.prototype.IsInPolygon=function(polygon)
{
var isInside = false;
var j = 0;
var x = this.longitude;
var y = this.latitude;
var paths = polygon.getLocations();
for (var i = 0; i < paths.length ; i++) {
j++;
if (j == paths.length) { j = 0; }
if (((paths[i].latitude < y) && (paths[j].latitude >= y)) || ((paths[j].latitude < y) && (paths[i].latitude >= y))) {
if (paths[i].longitude + (y - paths[i].latitude) / (paths[j].latitude - paths[i].latitude) * (paths[j].longitude - paths[i].longitude) < x) {
isInside = !isInside
}
}
}
return isInside;
};
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
credentials: 'YOUR KEY'
});
var center = map.getCenter();
var polygon = new Microsoft.Maps.Polygon([new Microsoft.Maps.Location(center.latitude - 0.05, center.longitude - 0.05),
new Microsoft.Maps.Location(center.latitude + 0.01, center.longitude - 0.05),
new Microsoft.Maps.Location(center.latitude + 0.01, center.longitude + 0.05)], { fillColor: 'yellow', strokeColor: 'orange',
strokeThickness: 5, strokeDashArray: [1, 2, 5, 10] });
map.entities.push(polygon);
var location = new Microsoft.Maps.Location(center.latitude, center.longitude);
alert("The location is inside in the polygon : " + location.IsInPolygon(polygon));
}
</script>
<script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?callback=load' async defer></script>
</body>
</html>

d3 stacked to grouped bar chart date axis

I am building a d3 function to build a grouped bar chart that can transition into a stacked bar chart. Everything was working fine until I tried to bring in date values for the x-axis. Now the chart only graphs one bar. The X axis shows correctly but the position of the rectangles seems to be off. My jsFiddle is posted at the bottom of the code.
function createChart(inputdata,chartname,inputtop,inputbottom,inputwidth,inputheight){
var stack = d3.layout.stack(),
layers = inputdata,
m = layers[0].length, // number of samples per layer
n = layers.length, // number of layers
data = stack(d3.range(n).map(function(d) { return layers[d]; }));
var yGroupMax = d3.max(data, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
yStackMax = d3.max(data, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: inputtop, right: 10, bottom: inputbottom, left: 10},
width = inputwidth - margin.left - margin.right,
height = inputheight - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var xTime = d3.time.scale()
.domain([new Date("2016-01-01"), d3.time.day.offset(new Date("2016-01-04"), 1)])
.rangeRound([0, width - margin.left - margin.right]);
var xAxisTime = d3.svg.axis()
.scale(xTime)
.orient('bottom')
.ticks(d3.time.days, 1)
.tickFormat(d3.time.format('%a %d'))
.tickSize(0)
.tickPadding(8);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(2)
.tickPadding(6)
.outerTickSize(0);
var svg = d3.select(chartname).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(data)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisTime)
svg.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(" + (Number(margin.left) + 14) + ",0)")
.call(yAxis);
svg.select("g.yaxis").selectAll(".tick")
.each(function (d) {
if ( d === 0 ) {
this.remove();
}
});
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
d3.select("input[value=\"0\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
if (this.value === "stacked") transitionStacked();
//else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
var allchart = d3.selectAll(".chart").selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll(".chart");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.duration(200)
.ease("linear")
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function (d) {
if ( d === 0 ) {
this.remove();
}
});
};
function transitionStacked() {
y.domain([0, yStackMax]);
var allchart = d3.selectAll(".chart").selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll(".chart");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.duration(200)
.ease("linear")
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand());
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function (d) {
if ( d === 0 ) {
this.remove();
}
});
};
};
JSFiddle Link
You've created a time scale in variable xTime, but you aren't using it to draw the rects:
var rect = layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return xTime(new Date(d.x)); //<-- don't use x here
});
Also, If you are using a time axis, I recommend changing your input data into datetimes instead of new Date( everywhere.
Finally, your selections are wrong in your transitions.
Here it is all fixed up:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 700px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
</head>
<body>
<form>
<label>
<input type="radio" name="mode" value="grouped" /> Grouped
</label>
<label>
<input type="radio" name="mode" value="stacked" checked="" /> Stacked
</label>
</form>
<chart_1></chart_1>
<script>
var layers = [{
"x": "2016-01-01",
"y": 4,
"z": 5
}, {
"x": "2016-01-02",
"y": 5,
"z": 6
}, {
"x": "2016-01-03",
"y": 6,
"z": 3
}, {
"x": "2016-01-04",
"y": 7,
"z": 1
}];
var converted = convertjson(layers, "x", ["y", "z"]);
createChart(converted, "chart_1", 40, 20, 700, 550);
function createChart(inputdata, chartname, inputtop, inputbottom, inputwidth, inputheight) {
var stack = d3.layout.stack(),
layers = inputdata,
m = layers[0].length, // number of samples per layer
n = layers.length, // number of layers
data = stack(d3.range(n).map(function(d) {
return layers[d];
}));
var yGroupMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y;
});
}),
yStackMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y0 + d.y;
});
});
var margin = {
top: inputtop,
right: 10,
bottom: inputbottom,
left: 10
},
width = inputwidth - margin.left - margin.right,
height = inputheight - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var xTime = d3.time.scale()
.domain([new Date("2016-01-01"), d3.time.day.offset(new Date("2016-01-04"), 1)])
.rangeRound([0, width - margin.left - margin.right]);
var xAxisTime = d3.svg.axis()
.scale(xTime)
.orient('bottom')
.ticks(d3.time.days, 1)
.tickFormat(d3.time.format('%a %d'))
.tickSize(0)
.tickPadding(8);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(2)
.tickPadding(6)
.outerTickSize(0);
var svg = d3.select(chartname).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(data)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
return color(i);
});
var rect = layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisTime)
svg.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(" + (Number(margin.left) + 14) + ",0)")
.call(yAxis);
svg.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
d3.selectAll("input").on("change", change);
/*
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
d3.select("input[value=\"0\"]").property("checked", true).each(change);
}, 2000);
*/
function change() {
//clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
if (this.value === "stacked") transitionStacked();
//else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
var allchart = d3.selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll(".chart");
console.log(allchart)
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
return xTime(d.x) + x.rangeBand() / n * j;
})
.attr("width", x.rangeBand() / n)
.transition()
.duration(200)
.ease("linear")
.attr("y", function(d) {
return y(d.y);
})
.attr("height", function(d) {
return height - y(d.y);
});
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
};
function transitionStacked() {
y.domain([0, yStackMax]);
var allchart = d3.selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll(".chart");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.transition()
.duration(200)
.ease("linear")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("width", x.rangeBand());
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
};
};
function convertjson(data, xValue, yArray) {
var arrayconvertedjson = [];
var convertedjson = [];
for (var j = 0; j < yArray.length; j++) {
for (var i = 0; i < data.length; i++) {
convertedjson.push({
"x": new Date(data[i][xValue]),
"y": data[i][yArray[j]]
});
};
arrayconvertedjson.push(convertedjson)
convertedjson = [];
};
return arrayconvertedjson;
};
</script>
</body>
</html>

dygraph, display day beside date on x-axis

I have dygraph working.
Currently, x-axis granularity is 1 day and format displayed on axis is ddMMM, e.g. 02May.
I wish to display the day as well, e.g. Fri 02May.
How do I do this ?
Thank you
HTML page containing JavaScript:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Temperature(°C) vs Time</title>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.4.4rc2.js'></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type='text/javascript' src="http://dygraphs.com/dygraph-combined.js"></script>
<style type='text/css'>
</style>
<script type='text/javascript'>//<![CDATA[
$(document).ready(function () {
var r = [ ];
var base_time = Date.parse("2014/03/05");
var num = 24 * 0.25 * 365;
for (var i = 0; i < num; i++) {
r.push([ new Date(base_time + i * 3600 * 1000),
i + 50 * (i % 60), // line
i * (num - i) * 4.0 / num // parabola
]);
}
var orig_range = [ r[0][0].valueOf(), r[r.length - 1][0].valueOf() ];
// NEW CODE INSERTED - STARTS
var one_month_previous = new Date();
one_month_previous.setMonth(one_month_previous.getMonth() - 1);
var one_week_previous = new Date();
one_week_previous.setDate(one_week_previous.getDate()-7);
var three_days_previous = new Date();
three_days_previous.setDate(three_days_previous.getDate()-3);
var one_days_previous = new Date();
one_days_previous.setDate(one_days_previous.getDate()-1);
var twelve_hours_previous = new Date();
twelve_hours_previous.setHours(twelve_hours_previous.getHours() - 12);
// NEW CODE INSERTED - ENDS
var d_names = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
var m_names = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
g = new Dygraph(
document.getElementById("graphdiv3"),
"show_csv.php",
{
// NEW CODE INSERTED - STARTS
// dateWindow: [ Date.parse(one_month_previous) ,
// Date.parse(new Date()) ],
dateWindow: [ Date.parse(one_week_previous) ,
Date.parse(new Date()) ],
// dateWindow: [ Date.parse(three_days_previous) ,
// Date.parse(new Date()) ],
// dateWindow: [ Date.parse(one_days_previous) ,
// Date.parse(new Date()) ],
// dateWindow: [ Date.parse(twelve_hours_previous) ,
// Date.parse(new Date()) ],
// dateWindow: [ Date.parse("2014/03/01 12:00:00"),
// Date.parse("2014/03/31 12:00:00") ],
// NEW CODE INSERTED - ENDS
title: 'Temperature(°C) vs Time',
rollPeriod: 1,
showRoller: true,
xlabel: '',
ylabel: 'Temperature (°C)',
legend: 'always',
labelsKMB: 'true',
labelsSeparateLines: 'true',
colors: [
"rgb(51,204,204)",
"#00DD55",
"rgb(255,100,100)",
"rgba(50,50,200,0.4)"],
axes: {
x: {
axisLabelFormatter: function(d, gran) {
var curr_day = d_names[d.getDay()];
var curr_month = m_names[d.getMonth()];
return curr_day + " "
+ Dygraph.zeropad(d.getDate())
+ curr_month;
}
}
}
//
//
// below works
//
// axes: {
// x: {
// valueFormatter: function(x) {
// return 'text';
// },
// axisLabelFormatter: function(x) {
// return x;
// },
// }
// }
//
}
);
var desired_range = null;
function approach_range() {
if (!desired_range) return;
// go halfway there
var range = g.xAxisRange();
if (Math.abs(desired_range[0] - range[0]) < 60 &&
Math.abs(desired_range[1] - range[1]) < 60) {
g.updateOptions({dateWindow: desired_range});
// g.updateOptions(
// {dateWindow: desired_range,
//axes: {
// x: {
// axisLabelFormatter: function(d, gran) {
// var curr_day = d_names[d.getDay()];
// var curr_month = m_names[d.getMonth()];
// return curr_day + " "
// + Dygraph.zeropad(d.getDate())
// + curr_month;
// }
// }
// }
//});
// (do not set another timeout.)
} else {
var new_range;
new_range = [0.5 * (desired_range[0] + range[0]),
0.5 * (desired_range[1] + range[1])];
g.updateOptions({dateWindow: new_range});
// g.updateOptions(
//
//{dateWindow: new_range, axes: {
// x: {
// axisLabelFormatter: function(d, gran) {
// var curr_day = d_names[d.getDay()];
// var curr_month = m_names[d.getMonth()];
// return curr_day + " "
// + Dygraph.zeropad(d.getDate())
// + curr_month;
// }
// }
// }}
//);
animate();
}
}
function animate() {
setTimeout(approach_range, 50);
}
var zoom = function(res) {
var w = g.xAxisRange();
desired_range = [ w[0], w[0] + res * 1000 ];
animate();
}
var reset = function() {
desired_range = orig_range;
animate();
}
var pan = function(dir) {
var w = g.xAxisRange();
var scale = w[1] - w[0];
var amount = scale * 0.25 * dir;
desired_range = [ w[0] + amount, w[1] + amount ];
animate();
}
document.getElementById('hour').onclick = function() { zoom(3600); };
document.getElementById('day').onclick = function() { zoom(86400); };
document.getElementById('week').onclick = function() { zoom(604800); };
document.getElementById('month').onclick = function() { zoom(30 * 86400); };
document.getElementById('full').onclick = function() { reset(); };
document.getElementById('left').onclick = function() { pan(-1); };
document.getElementById('right').onclick = function() { pan(+1); };
}
);
//]]>
</script>
</head>
<body>
<center>
<div id="graphdiv3"
style="width:900px; height:400px;"></div>
</center>
<p>
<br>
<center>
<b>Zoom:</b>
<button id="hour">hour</button>
<button id="day">day</button>
<button id="week">week</button>
<button id="month">month</button>
<button id="full">full</button>
<br> or, click & drag. Double-click to zoom back out.
<br>
<b>Pan:</b>
<button id="left">left</button>
<button id="right">right</button>
or, shift & drag.
<br>
<b>Raw Data:</b>
<button onclick="window.location.href='show_csv_raw_data.php'">raw</button>
<!--
<b>Zoom:</b>
hour
day
week
month
full
<b>Pan:</b>
left
right
-->
<br>
<br>Measurements every 15 mins. Refresh with F5 button for update.
<br>Your last page refresh was on <b><span id="long_date"></span></b>
<script type='text/javascript'>
var datetime = new Date();
document.getElementById("long_date").innerHTML=datetime;
</script>
</center>
<br>
<br>
Notes:
<br> 1. Measurements before 19<sup>th</sup> March: Hall Cupboard was "Inside" sitting on Rasp Pi, Loft was just "Inside window", Hall was external temp measurement, "Outside window"
<br> 2. Mar 31<sup>st</sup>, green line, shows loft temp rise of ~8°C(8am) to 17°C(3pm), Δ ~ 9°C, in zero octave cloud/ direct sunlight conditions. Direct lift in hall temperature, red line, while heating off clear. Indicative that solar heating of loft as thermal barrier worthwhile.
<br> 3. April 3<sup>rd</sup>, red line, shows hall temp rise of ~14°C(17:45) to 20°C(19:45), Δ ~ 5°C, in two hours while loft temprature remains fairly constant (flat) ie no solar gain. Shows heating performance of approximately 2.5°C per hour
<br> 4. April 20<sup>th</sup>. Raw data can be viewed.
<br> 5. April 29<sup>th</sup>. Heating is off: the evening of Friday 25<sup>th</sup>, all of Sat 26<sup>th</sup>, all of Sun 27<sup>th</sup> and early morning Mon 28<sup>th</sup>. Consequences of heating off can be seen.
<br>6. April 29<sup>th</sup>. Loft space (green line) heats, with keroscene space heating off, through black colour concerete tiles, in zero octave cloud/ direct sunlight conditions. Solar gain is
<dl>
<dd>o Loft temp rise of ~7 °C(6:30am) to 20.9°C(5:30pm), Δ ~ 14°C, <i>and</i> </dd>
<dd>o Hall way temp rise of ~11.4°C(6:30am) to 15.6°C(5:30pm), Δ ~ 4°C </dd>
<br>
<dd> Conversion = 0.3°C rise in Hall way temperature per 1.0°C rise in loft space temperature</dd>
<br>
<dd>See solar (black-body) space heating ideas http://bit.ly/solar-air-heating </dd>
</dl>
7.
</p>
<div id="div_g"></div>
<br>
<br>
<iframe width="300" height="200" style="border: 1px solid #cccccc;" src="http://api.thingspeak.com/channels/11286/charts/3?width=450&height=260&results=1000&dynamic=true&yaxis=Hall&title=Hall%20Temp" ></iframe>
<iframe width="300" height="200" style="border: 1px solid #cccccc;" src="http://api.thingspeak.com/channels/11286/charts/1?width=450&height=260&results=1000&dynamic=true&yaxis=Hall%20Cupboard&title=Hall%20Cupboard%20Temp" ></iframe>
<iframe width="300" height="200" style="border: 1px solid #cccccc;" src="http://api.thingspeak.com/channels/11286/charts/2?width=450&height=260&results=1000&dynamic=true&yaxis=Loft&title=Loft%20Temp" ></iframe>
</body>
</html>
use the axisLabelFormatter property and format the date as desired
var d_names = ["Sun","Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"];
var m_names = ["Jan", "Fe", "Mar",
"Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Novr", "Dec"];
var g3 = new Dygraph(
document.getElementById("g_div"),
DailyData(),
{
xAxisLabelWidth: 80,
axisLabelFontSize: 12,
width: 640,
height: 350,
axes: {
x: {
axisLabelFormatter: function(d, gran) {
var curr_day = d_names[d.getDay()];
var curr_month = m_names[d.getMonth()];
return curr_day + " "
+ Dygraph.zeropad(d.getDate())
+ curr_month;
}
}
}
});
Here is a DEMO