The earlier example of this brushable radial chart led to some feedback to make the filtering better. I introduced a #circularBrush.filter(array,accessor) that takes an array of data and and accessor for that data and returns to you the data that falls into the area of the brush. The functionality of this chart is no different than the other, but if you take a look at the code, it's much more efficient.
forked from emeeks's block: circularbrush.filter
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<link href='https://fonts.googleapis.com/css?family=Lato:300' rel='stylesheet' type='text/css'>
<style>
body {
background-color: whitesmoke;
}
.extent {
fill-opacity: .1;
fill: rgb(205,130,42);
cursor: move;
}
.e {
fill: rgb(111,111,111);
cursor: move;
}
.w {
fill: rgb(169,169,169);
cursor: move;
}
.freeze {
fill: #A8DFE4;
}
.rain {
fill: #209CD3;
}
.clear {
fill: white
}
.scattered {
fill: lightgray;
}
.cloudy {
fill: #a1a1a1;
}
.overcast {
fill: #616161;
}
svg {
background-color: white;
font-family: 'Lato';
}
.axis {
stroke: white;
opacity: .8;
}
text {
pointer-events: none;
}
text.title {
font-size: 26px;
}
text.months, text.temp {
text-anchor: middle;
font-size: 12px;
fill: #39837B;
}
circle.axis {
stroke: white;
stroke-width: 1px;
fill: none;
}
circle.axis.record {
stroke: #bae0d6;
stroke-width: 1.2px;
opacity: 1;
}
line.record, line.avg, line.yearLow, line.yearHigh{
stroke-width: 2px;
}
line.record {
stroke: #bae0d6;
}
line.avg {
stroke: #3FA39E;
opacity: .5;
}
line.year {
stroke: #006358;
}
line.yearLow, line.yearHigh{
stroke: #F97F5A;
}
.avg {
stroke: #3FA39E;
fill: #3FA39E;
}
.record {
stroke: #bae0d6;
fill: #bae0d6;
.opacity: .5;
}
.year {
stroke: #F97F5A;
fill: #F97F5A;
}
.beyond {
stroke: #445E5B;
fill: #445E5B;
}
</style>
</head>
<body>
<svg width=960 height=500></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.3.0/d3-legend.min.js"></script>
<script src="d3.svg.circularbrush.js" charset="utf-8" type="text/javascript"></script>
<script>
var width = 960,
margin = 20,
height = 500,
svg = d3.select('svg'),
origin = svg.append('g')
.attr('transform', 'translate(' + width*3/5 + ',' + height/2 + ')'),
rScale = d3.scale.linear()
.domain([-10, 110])
.range([0, height/2 - margin]),
yScale = function (day, temp) {return -Math.cos(angleScale(day)*Math.PI/180)*rScale(parseInt(temp))},
xScale = function (day, temp) {return Math.sin(angleScale(day)*Math.PI/180)*rScale(parseInt(temp))},
angleScale = d3.scale.linear()
.range([0, 360]);
var drawRadial = function(chart, cl, data, low, high){
chart.selectAll('line.' + cl)
.data(data)
.enter().append('line')
.attr('x1', function (d) {return xScale(d.index, d[low])})
.attr('x2', function (d) {return xScale(d.index, d[high])})
.attr('y1', function (d) {return yScale(d.index, d[low])})
.attr('y2', function (d) {return yScale(d.index, d[high])})
.attr('class', cl);
};
d3.json('ny.json', function(err, json){
rawData = json;
angleScale.domain([0, json.values.length - 1]);
var min = d3.min(json.values, function (d) {return parseInt(d.recLow)}),
max = d3.max(json.values, function (d) {return parseInt(d.recHigh)});
var months = [];
//find index for months based on data
json.values.forEach(function (d, i) {
var month = d.date.split('-')[1],
prevDaysMonth = ( i === 0 ) ? undefined : json.values[i - 1].date.split('-')[1];
if (i === 0 || month != prevDaysMonth){
months.push({
month: month,
index: i
});
}
})
//circle axis
origin.selectAll('circle.axis-green')
.data([40, 60, 80, 100])
.enter().append('circle')
.attr('r', function (d) {return rScale(d)})
.attr('class', 'axis record')
//record low and high
drawRadial(origin, 'record', json.values, 'recLow', 'recHigh')
//avg low and high
drawRadial(origin, 'avg', json.values, 'avgLow', 'avgHigh')
//this year's temperature
var thisYear = json.values.filter(function (d) {return d.min });
drawRadial(origin, 'year', thisYear, 'min', 'max')
var lowLower = json.values.filter(function (d) {return d.min && parseInt(d.min) < parseInt(d.avgLow)});
drawRadial(origin, 'yearLow', lowLower, 'min', 'avgLow')
var highHigher = json.values.filter(function (d) {return d.min && parseInt(d.max) > parseInt(d.avgHigh)});
drawRadial(origin, 'yearHigh', highHigher, 'max', 'avgHigh')
var circleAxis = [0, 32, 60, 80, 100]
circleAxis = circleAxis.map( function (d) {return {temp: d, index: 320}})
//temperature axis
origin.selectAll('circle.axis-white')
.data(circleAxis)
.enter().append('circle')
.attr('r', function (d) {return rScale(d.temp)})
.attr('class', 'axis')
//temperature axis labels
origin.selectAll('text.temp')
.data(circleAxis)
.enter().append('text')
.attr('x', function (d) {
return xScale(d.index, d.temp)})
.attr('y', function (d) {return yScale(d.index, d.temp)})
.text(function (d) {return d.temp + '°'})
.attr('class', 'temp');
//axis lines
var axis = origin.append('g');
axis.selectAll('line.axis')
.data(months)
.enter().append('line')
.attr('x2', function (d) {
return xScale(d.index, 120)})
.attr('y2', function (d) {return -yScale(d.index, 120)})
.attr('class', 'axis');
var monthLabels = months.filter( function (d,i) {return i%3 === 0})
//month labels
axis.selectAll('text.months')
.data(monthLabels)
.enter().append('text')
.attr('x', function (d) {
return xScale(d.index, 110)})
.attr('y', function (d) {return yScale(d.index, 110)})
.text(function (d) {return d.month})
.attr('class', 'months');
//center for reference
axis.append('circle')
.attr('r', 3)
.attr('class', 'avg')
//title
svg.append('text')
.attr('x', 30)
.attr('y', 60)
.text(json.name)
.attr('class', 'title')
//subtitle
svg.append('text')
.attr('x', 30)
.attr('y', 100)
.text('Historical Weather Data')
//create legend
var legendScale = d3.scale.ordinal()
.domain(['Record', 'Average', 'This Year - within avg', 'This Year - beyond avg', 'Freezing', 'Precipitation', 'Scattered Clouds', "Cloudy", "Overcast"])
.range(['record', 'avg', 'beyond', 'year', 'freeze', 'rain', 'scattered', 'cloudy', 'overcast'])
//d3-legend
var legend = d3.legend.color()
.shapePadding(5)
.useClass(true)
.scale(legendScale);
svg.append('g')
.attr('transform', 'translate(30,120)')
.call(legend);
d3.json("cloud_rain_freeze.json", loadBars);
});
function loadBars(data) {
var freezeBars = [];
var rainBars = [];
var cloudBars = [];
var freeze = {};
var cloud = {start: data[0].date, category: data[0].cloud};
var rain = {};
data.forEach(function (d, i) {
if (d.cloud !== cloud.category) {
cloud.end = d.date;
cloudBars.push(cloud);
cloud = {start: d.date, category: d.cloud};
}
if (freeze.start && !d.freeze) {
freeze.end = d.date;
freezeBars.push(freeze);
freeze = {};
}
else if (d.freeze && !freeze.start) {
freeze.start = d.date;
}
if (rain.start && !d.rain) {
rain.end = d.date;
rainBars.push(rain);
rain = {};
}
else if (d.rain && !rain.start) {
rain.start = d.date;
}
});
drawBars(rainBars, "rain", 205);
drawBars(freezeBars, "freeze", 200);
drawBars(cloudBars, "freeze", 210);
}
function loadBars(data) {
freezeBars = [];
rainBars = [];
cloudBars = [];
var freeze = {};
var cloud = {start: data[0].date, category: data[0].cloud};
var rain = {};
var dateScale = d3.time.scale().domain([new Date("01/01/2015"), new Date("12/31/2015")]).range([1,366]);
data.forEach(function (d, i) {
if (d.cloud !== cloud.category) {
cloud.end = d.date;
cloud.endInt = dateScale(new Date(d.date));
cloudBars.push(cloud);
cloud = {start: d.date, startInt: dateScale(new Date(d.date)), category: d.cloud};
}
if (freeze.start && !d.freeze) {
freeze.end = d.date;
freeze.endInt = dateScale(new Date(d.date));
freezeBars.push(freeze);
freeze = {};
}
else if (d.freeze && !freeze.start) {
freeze.start = d.date;
freeze.startInt = dateScale(new Date(d.date));
}
if (rain.start && !d.rain) {
rain.end = d.date;
rain.endInt = dateScale(new Date(d.date));
rainBars.push(rain);
rain = {};
}
else if (d.rain && !rain.start) {
rain.start = d.date;
rain.startInt = dateScale(new Date(d.date));
}
});
drawBars(rainBars, "rain", 205);
drawBars(freezeBars, "freeze", 200);
drawBars(cloudBars, "cloud", 210);
drawBrush();
}
function drawBrush() {
brush = d3.svg.circularbrush()
.range([1,366])
.innerRadius(10)
.outerRadius(218)
.handleSize(0.1)
.extent([30,120])
.on("brush", brushed);
d3.select("svg")
.append("g")
.attr("class", "brush")
.attr("transform", "translate(576,250)")
.call(brush);
d3.select("svg").append("g")
.attr("class", "linear")
.attr("transform", "translate(40,350)");
brushed();
function brushed() {
filteredData = brush.filter(rawData.values, function (d) {return d.index});
var dates = filteredData.map(function (d) {return d.date});
var dateScale = d3.scale.ordinal()
.domain(dates)
.range(dates.map(function (d, i) {return i}));
filteredRainbars = brush.filter(rainBars, function (d) {return d.startInt});
filteredCloudbars = brush.filter(cloudBars, function (d) {return d.startInt});
filteredFreezebars = brush.filter(freezeBars, function (d) {return d.startInt});
var yScale = d3.scale.linear().domain([-10,110]).range([100,0]).clamp(true);
var xScale = d3.scale.linear().domain([0, filteredData.length]).range([0,250]);
var lineWidth = 250 / filteredData.length;
d3.select("g.linear")
.selectAll("*")
.remove();
d3.select("g.linear")
.append("g")
.attr("class", "weatherbars")
.selectAll("g")
.data([{class: "rain", data: filteredRainbars}, {class: "cloud", data: filteredCloudbars}, {class: "freeze", data: filteredFreezebars}])
.enter()
.append("g")
.each(function (g, gi) {
console.log(this,g)
d3.select(this)
.selectAll("rect")
.data(g.data)
.enter()
.append("rect")
.attr("class", function (d) {return g.class + " " + d.category})
.attr("y", gi * -10)
.attr("x", function (d) {return xScale(dateScale(toDate(d))) })
.attr("width", function (d) {return (d.endInt - d.startInt) * lineWidth })
.attr("height", "5px")
})
d3.select("g.linear")
.selectAll("g.linearBars")
.data(filteredData, function (d) {return d.date})
.enter()
.insert("g", "g.weatherbars")
.attr("class", "linearBars")
.each(function (d, i) {
if (i === 0 || i === filteredData.length - 1) {
d3.select(this).append("text")
.text(d.date)
.attr("y", -30)
.style("text-anchor", "middle");
}
d3.select(this).append("line").style("stroke-width", lineWidth).attr("class", "record")
d3.select(this).append("line").style("stroke-width", lineWidth).attr("class", "avg")
d3.select(this).append("line").style("stroke-width", lineWidth).attr("class", "yearLow")
d3.select(this).append("line").style("stroke-width", lineWidth).attr("class", "yearHigh")
d3.select(this).append("line").style("stroke-width", lineWidth).attr("class", "year")
});
d3.selectAll("g.linearBars")
.attr("transform", function (d,i) {return "translate(" + xScale(dateScale(d.date)) +",0)" });
d3.selectAll("g.linearBars")
.each(function (d) {
d3.select(this).select("line.record")
.attr("y1", yScale(parseInt(d.recHigh)))
.attr("y2", yScale(parseInt(d.recLow)));
d3.select(this).select("line.avg")
.attr("y1", yScale(parseInt(d.avgHigh)))
.attr("y2", yScale(parseInt(d.avgLow)));
if (d.max != null) {
if (d.min < parseInt(d.avgLow)) {
d3.select(this).select("line.yearLow")
.attr("y1", yScale(parseInt(d.min)))
.attr("y2", yScale(parseInt(d.avgLow)));
}
if (d.max > parseInt(d.avgHigh)) {
d3.select(this).select("line.yearHigh")
.attr("y1", yScale(parseInt(d.max)))
.attr("y2", yScale(parseInt(d.avgHigh)));
}
if (!(d.min > parseInt(d.avgHigh) || d.max < parseInt(d.avgLow))) {
d3.select(this).select("line.year")
.attr("y1", yScale(Math.max(d.min,parseInt(d.avgLow))))
.attr("y2", yScale(Math.min(d.max,parseInt(d.avgHigh))));
}
}
})
}
}
function toDate(d) {
var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
var date = new Date(d.start);
return date.getDate() + "-" + monthNames[date.getMonth()];
}
function drawBars(data, type, offset) {
dateScale = d3.scale.linear()
.domain([1,366])
.range([0,(2 * Math.PI)]);
var arc = d3.svg.arc().innerRadius(offset).outerRadius(offset + 5);
d3.select("svg").append("g")
.attr("class", type + "bars")
.attr("transform", "translate(576,250)")
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("d", drawArc)
.attr("class", function (d) {return type + " " + d.category})
function drawArc(d) {
projected = {startAngle: dateScale(d.startInt), endAngle: dateScale(d.endInt) };
return arc(projected);
}
}
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.3.0/d3-legend.min.js