block by emeeks 2fffa9abe50ac97603c7

Brushable Radial Chart

Full Screen

A modification of Susie Lu’s radial weather plot. This one shows periods of cloudiness, precipitation and freezing temperatures via the bars around the border. It also includes a circular brush that lets you select a band on the radial chart. The selected band is then displayed in a more traditional linear way on the bottom left.

The original readme explains what’s going on with the radial chart:

Weather Plot - New York 2015

In the example we’re looking at historical weather data for New York provided by intellicast.com and wunderground.com. Inspired by weather-radicals.com.

This example uses scales to roll your own radial projection by mapping out the x, y, and r positions. If you are creating a line or an area you can use d3’s convenience functions d3.svg.line.radial and d3.svg.area.radial but this is a method you can use if you want to use different graphical elements in a circular layout.

index.html

<!DOCTYPE html>
<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;
      -moz-user-select: none;
      -webkit-user-select: none;
      -ms-user-select: 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)
      .on("brush", brush);

  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)")

  function brush() {
    extent = brush.extent();
    var yScale = d3.scale.linear().domain([-10,110]).range([100,0]).clamp(true);
    var xScale = d3.scale.linear().domain([1,366]).range([0,250]);

    var start = extent[0];
    var end = extent[1];

    var barOffset = 0;

    if (start < end) {
      filteredData = rawData.values.filter(function (d) {
         return d.index >= start && d.index <= end;
      });
      filteredRainbars = rainBars.filter(function (d) {
        var sDate = d.startInt;
        var eDate = d.endInt;
        return (eDate <= end && eDate >= start) || (sDate <= end && sDate >= start)
      })
      filteredFreezebars = freezeBars.filter(function (d) {
        var sDate = d.startInt;
        var eDate = d.endInt;
        return (eDate <= end && eDate >= start) || (sDate <= end && sDate >= start)
      })
      filteredCloudbars = cloudBars.filter(function (d) {
        var sDate = d.startInt;
        var eDate = d.endInt;
        return (eDate <= end && eDate >= start) || (sDate <= end && sDate >= start)
      })

    }
    else {
        var janone = 1;
        var decthirtyone = 366
      var filteredDataEarly = rawData.values.filter(function (d) {
         return (d.index >= start && d.index <= decthirtyone);
      });
      var filteredDataAfter = rawData.values.filter(function (d) {
         return (d.index <= end && d.index >= janone);
      });

      barOffset = filteredDataEarly.length;
      earlyMin = d3.min(filteredDataEarly, function (d) {return d.index})

      filteredFreezebarsEarly = beforeBars(freezeBars)
      filteredFreezebarsAfter = afterBars(freezeBars)
      filteredRainbarsEarly = beforeBars(rainBars)
      filteredRainbarsAfter = afterBars(rainBars)
      filteredCloudbarsEarly = beforeBars(cloudBars);
      filteredCloudbarsAfter = afterBars(cloudBars);

      filteredData = filteredDataEarly.concat(filteredDataAfter);
      filteredFreezebars = filteredFreezebarsEarly.concat(filteredFreezebarsAfter);
      filteredRainbars = filteredRainbarsEarly.concat(filteredRainbarsAfter);
      filteredCloudbars = filteredCloudbarsEarly.concat(filteredCloudbarsAfter);

      function beforeBars(bars) {
        return bars.filter(function (d) {
        var sDate = d.startInt;
        var eDate = d.endInt;
        (d.index >= start && d.index <= decthirtyone)
        return (eDate >= start && eDate <= decthirtyone) || (sDate >= start && sDate <= decthirtyone)
      }).map(function (d) {
        return {startInt: d.startInt - earlyMin, endInt: d.endInt - earlyMin, category: d.category}
      })
    }

      function afterBars(bars) {
        return bars.filter(function (d) {
        var sDate = d.startInt;
        var eDate = d.endInt;
        (d.index >= start && d.index <= decthirtyone)
        return (eDate <= end && eDate >= janone) || (sDate <= end && sDate >= janone)
      }).map(function (d) {
        return {startInt: d.startInt + barOffset, endInt: d.endInt + barOffset, category: d.category}
      })
      }

    }

    var lineWidth = 250 / filteredData.length;

    minDate = d3.min(filteredData, function (d) {return d.index})
    maxDate = d3.max(filteredData, function (d) {return d.index})

    xScale.domain([0, filteredData.length]);

    d3.select("g.linear")
    .selectAll("g.linearBars")
    .remove();

    d3.select("g.linear")
    .selectAll("rect")
    .remove();

    d3.select("g.linear")
    .selectAll("text")
    .remove();

    d3.select("g.linear")
    .selectAll("rect.rainbars")
    .data(filteredRainbars)
    .enter()
    .append("rect")
    .attr("class", "rain")
    .attr("y", -10)
    .attr("x", function (d) {return xScale((d.startInt - minDate)) })
    .attr("width", function (d) {return Math.min(250 - xScale((d.startInt - minDate)), (d.endInt - d.startInt) * lineWidth) })
    .attr("height", "5px")

    d3.select("g.linear")
    .selectAll("rect.freezebars")
    .data(filteredFreezebars)
    .enter()
    .append("rect")
    .attr("class", "freeze")
    .attr("y", -0)
    .attr("x", function (d) {return xScale((d.startInt - minDate)) })
    .attr("width", function (d) {return Math.min(250 - xScale((d.startInt - minDate)), (d.endInt - d.startInt) * lineWidth) })
    .attr("height", "5px")

    d3.select("g.linear")
    .selectAll("rect.cloudbars")
    .data(filteredCloudbars)
    .enter()
    .append("rect")
    .attr("class", function (d) {return d.category })
    .attr("y", -20)
    .attr("x", function (d) {return xScale((d.startInt - minDate)) })
    .attr("width", function (d) {return Math.min(250 - xScale((d.startInt - minDate)), (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", "rect")
    .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", "highlightline")
     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.select(this).append("line").style("stroke-width", lineWidth).attr("class", "hoverline")
    });

    d3.selectAll("g.linearBars")
    .attr("transform", function (d,i) {return "translate(" + xScale(i) +",0)" });

    d3.selectAll("g.linearBars")
    .each(function (d) {
      var thisG = this;
      d3.select(this).select("line.highlightline")
        .attr("y1", -30)
        .attr("y2", 100)
        .style("stroke-width", 1)
        .style("stroke", "black")
        .style("opacity", 0);
      d3.select(this).select("line.hoverline")
        .attr("y1", -30)
        .attr("y2", 100)
        .style("stroke-width", lineWidth)
        .style("opacity", 0.0)
        .style("stroke", "black")
        .on("mouseover", function () {
          d3.select(thisG).select("line.highlightline").style("opacity", 1);
        })
        .on("mouseout", function () {
          d3.selectAll("line.highlightline").style("opacity", 0);
        })

      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 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>

d3.svg.circularbrush.js

d3.svg.circularbrush = function() {
	var _extent = [0,Math.PI * 2];
    var _circularbrushDispatch = d3.dispatch('brushstart', 'brushend', 'brush');
	var _arc = d3.svg.arc().innerRadius(50).outerRadius(100);
	var _brushData = [
		{startAngle: _extent[0], endAngle: _extent[1], class: "extent"},
		{startAngle: _extent[0] - .2, endAngle:  _extent[0], class: "resize e"},
		{startAngle: _extent[1], endAngle: _extent[1] + .2, class: "resize w"}
		];
	var _newBrushData = [];
	var d3_window = d3.select(window);
	var _origin;
	var _brushG;
	var _handleSize = .2;
	var _scale = d3.scale.linear().domain(_extent).range(_extent);

	function _circularbrush(_container) {

		_brushG = _container
		.append("g")
		.attr("class", "circularbrush");

_brushG
.selectAll("path.circularbrush")
.data(_brushData)
.enter()
.insert("path", "path.resize")
.attr("d", _arc)
.attr("class", function(d) {return d.class + " circularbrush"})

_brushG.select("path.extent")
.on("mousedown.brush", resizeDown)

_brushG.selectAll("path.resize")
.on("mousedown.brush", resizeDown)

		return _circularbrush;
	}

	_circularbrush.extent = function(_value) {
		var _d = _scale.domain();
		var _r = _scale.range();

		var _actualScale = d3.scale.linear()
		.domain([-_d[1],_d[0],_d[0],_d[1]])
		.range([_r[0],_r[1],_r[0],_r[1]])

		if (!arguments.length) return [_actualScale(_extent[0]),_actualScale(_extent[1])];

		_extent = [_scale.invert(_value[0]),_scale.invert(_value[1])];
		return this
	}

	_circularbrush.handleSize = function(_value) {
		if (!arguments.length) return _handleSize;

		_handleSize = _value;
		_brushData = [
		{startAngle: _extent[0], endAngle: _extent[1], class: "extent"},
		{startAngle: _extent[0] - _handleSize, endAngle:  _extent[0], class: "resize e"},
		{startAngle: _extent[1], endAngle: _extent[1] + _handleSize, class: "resize w"}
		];
		return this
	}

	_circularbrush.innerRadius = function(_value) {
		if (!arguments.length) return _arc.innerRadius();

		_arc.innerRadius(_value);
		return this
	}

	_circularbrush.outerRadius = function(_value) {
		if (!arguments.length) return _arc.outerRadius();

		_arc.outerRadius(_value);
		return this
	}

	_circularbrush.range = function(_value) {
		if (!arguments.length) return _scale.range();

		_scale.range(_value);
		return this
	}

	_circularbrush.arc = function(_value) {
		if (!arguments.length) return _arc;

		_arc = _value;
		return this

	}

    d3.rebind(_circularbrush, _circularbrushDispatch, "on");

	return _circularbrush;

	function resizeDown(d) {
		var _mouse = d3.mouse(_brushG.node());

		_originalBrushData = {startAngle: _brushData[0].startAngle, endAngle: _brushData[0].endAngle};

		_origin = _mouse;

		if (d.class == "resize e") {
			d3_window
			.on("mousemove.brush", function() {resizeMove("e")})
			.on("mouseup.brush", extentUp);
		}
		else if (d.class == "resize w") {
			d3_window
			.on("mousemove.brush", function() {resizeMove("w")})
			.on("mouseup.brush", extentUp);			
		}
		else {
			d3_window
			.on("mousemove.brush", function() {resizeMove("extent")})
			.on("mouseup.brush", extentUp);
		}

		_circularbrushDispatch.brushstart();

	}

	function resizeMove(_resize) {
		var _mouse = d3.mouse(_brushG.node());
		var _current = Math.atan2(_mouse[1],_mouse[0]);
		var _start = Math.atan2(_origin[1],_origin[0]);

		if (_resize == "e") {
			var clampedAngle = Math.max(Math.min(_originalBrushData.startAngle + (_current - _start), _originalBrushData.endAngle), _originalBrushData.endAngle - (2 * Math.PI));

			if (_originalBrushData.startAngle + (_current - _start) > _originalBrushData.endAngle) {
				clampedAngle = _originalBrushData.startAngle + (_current - _start) - (Math.PI * 2);
			}
			else if (_originalBrushData.startAngle + (_current - _start) < _originalBrushData.endAngle - (Math.PI * 2)) {
				clampedAngle = _originalBrushData.startAngle + (_current - _start) + (Math.PI * 2);
			}

			var _newStartAngle = clampedAngle;
			var _newEndAngle = _originalBrushData.endAngle;			
		}
		else if (_resize == "w") {
			var clampedAngle = Math.min(Math.max(_originalBrushData.endAngle + (_current - _start), _originalBrushData.startAngle), _originalBrushData.startAngle + (2 * Math.PI))

			if (_originalBrushData.endAngle + (_current - _start) < _originalBrushData.startAngle) {
				clampedAngle = _originalBrushData.endAngle + (_current - _start) + (Math.PI * 2);
			}
			else if (_originalBrushData.endAngle + (_current - _start) > _originalBrushData.startAngle + (Math.PI * 2)) {
				clampedAngle = _originalBrushData.endAngle + (_current - _start) - (Math.PI * 2);
			}

			var _newStartAngle = _originalBrushData.startAngle;
			var _newEndAngle = clampedAngle;
		}
		else {
			var _newStartAngle = _originalBrushData.startAngle + (_current - _start * 1);
			var _newEndAngle = _originalBrushData.endAngle + (_current - _start * 1);
		}


		_newBrushData = [
		{startAngle: _newStartAngle, endAngle: _newEndAngle, class: "extent"},
		{startAngle: _newStartAngle - _handleSize, endAngle: _newStartAngle, class: "resize e"},
		{startAngle: _newEndAngle, endAngle: _newEndAngle + _handleSize, class: "resize w"}
		]


		_brushG
			.selectAll("path.circularbrush")
			.data(_newBrushData)
			.attr("d", _arc)

		if (_newStartAngle > (Math.PI * 2)) {
			_newStartAngle = (_newStartAngle - (Math.PI * 2));
		}
		else if (_newStartAngle < -(Math.PI * 2)) {
			_newStartAngle = (_newStartAngle + (Math.PI * 2));
		}

		if (_newEndAngle > (Math.PI * 2)) {
			_newEndAngle = (_newEndAngle - (Math.PI * 2));
		}
		else if (_newEndAngle < -(Math.PI * 2)) {
			_newEndAngle = (_newEndAngle + (Math.PI * 2));
		}

		_extent = ([_newStartAngle,_newEndAngle]);

		_circularbrushDispatch.brush();

	}

	function extentUp() {

		_brushData = _newBrushData;
		d3_window.on("mousemove.brush", null).on("mouseup.brush", null);

		_circularbrushDispatch.brushend();
	}


}