コンテキストをブラッシングした後にフォーカス部分をパンできるようにしたい d3 フォーカス/コンテキスト チャートがあり、コンテキスト エリアのブラシをかけられたセクションをフォーカス エリアのパンと同期して移動させたいと考えています。ただし、コンテキスト チャートで領域を選択した後にフォーカス部分をクリックすると、フォーカス スケールが変更され、ポイントが正しい座標に表示されなくなります。次のコードもjsfiddleで利用できます。
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title> - jsFiddle demo</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<style type="text/css">
circle {
-webkit-transition: fill-opacity 250ms linear;
}
.selecting circle {
fill-opacity: .5;
}
.selecting circle.selected {
stroke: #f00;
}
.brush .extent {
stroke: #B8C6D0;
fill-opacity: .125;
shape-rendering: crispEdges;
}
#context .axis path.domain {
stroke: lightsteelblue;
stroke-width: 5px;
}
#context .tick {
stroke:black;
stroke-width: 1px;
}
#context .x .tick {
stroke:black;
stroke-width: 2px;
}
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path {
stroke: #999;
stroke-width: 2px;
}
</style>
<script type="text/javascript">//<![CDATA[
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "3",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}, {
Id: "4",
Year: 1970,
Relevance: 45,
Category: "Cat1",
SpecFreq: 17,
GenFreq: 60
}, {
Id: "5",
Year: 1985,
Relevance: 90,
Category: "Cat1",
SpecFreq: 17,
GenFreq: 25
}];
$(function () {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
};
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
var focusAxisOptions = {
x: {
ticks: {
format: d3.format("d"),
size: -1* (500 - margin.top - margin.bottom),
ticks: 10
},
showLabel: true
},
y: {
ticks: {
format: d3.format(""),
size: -1 * (800 - margin.left - margin.right),
ticks: 10
},
showLabel: true
}
};
var contextAxisOptions = {
x: {
ticks: {
format: d3.format("d"),
size: -1 * (100 - margin.top - margin.bottom),
ticks: 10
},
showLabel: true
},
y: {
ticks: {
format: "",
size: 0,
ticks: 0
},
showLabel: false
}
};
var focus = DrawChart(data, margin, 800 - margin.left - margin.right, 500 - margin.top - margin.bottom, extentX, extentY, focusAxisOptions);
var context = DrawChart(data, margin, 800 - margin.left - margin.right, 100 - margin.top - margin.bottom, extentX, extentY, contextAxisOptions);
MakeContextBrushable(context, focus);
MakeFocusZoomable(focus);
});
function DrawChart(data, margin, width, height, extentX, extentY, axisOptions) {
//pad extents to provide some extra "blank" areas around edge of graph
var paddedExtentX = [extentX[0] - 5, extentX[1] +5];
var paddedExtentY = [extentY[0] - 5, extentY[1] +5];
//scales
var x = d3.scale.linear().domain(paddedExtentX).range([0, width]);
var y = d3.scale.linear().domain(paddedExtentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(axisOptions.x.ticks.format).tickSize(axisOptions.x.ticks.size).ticks(axisOptions.x.ticks.ticks);
var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(axisOptions.y.ticks.format).tickSize(axisOptions.y.ticks.size).ticks(axisOptions.y.ticks.ticks);
//create and size svg element
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("float", "left")
.style("clear", "left");
var g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); ;
// Add the x-axis.
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
g.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
// Add the x-axis label.
if (axisOptions.x.showLabel) {
g.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
}
// Add the y-axis label.
if (axisOptions.y.showLabel) {
g.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
}
//plot genFreq
var gGenerally = g.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = g.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
var chart = {
svg: svg,
g: g,
x: x,
y: y,
xAxis: xAxis,
yAxis: yAxis
}
return chart;
}
function MakeContextBrushable(context, focus) {
var brush = d3.svg.brush().x(context.x).y(context.y)
.on("brushstart", brushstart)
.on("brush", brushmove)
.on("brushend", brushend);
context.g.append("g")
.attr("class", "brush")
.call(brush);
function brushstart() {
context.svg.classed("selecting", true);
}
function brushmove() {
var e = d3.event.target.extent();
var circle = context.svg.selectAll("circle");
circle.classed("selected", function (d) {
return e[0][0] <= d["DecisionYear"] && d["DecisionYear"] <= e[1][0]
&& e[0][1] <= d["Relevance"] && d["Relevance"] <= e[1][1];
});
}
function brushend() {
context.svg.classed("selecting", !d3.event.target.empty());
if (!d3.event.target.empty()) {
var e = d3.event.target.extent();
focus.x.domain([e[0][0], e[1][0]]);
focus.y.domain([e[0][1], e[1][1]]);
focus.g.select(".x.axis").call(focus.xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.g.select(".y.axis").call(focus.yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
var circle = focus.svg.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
.attr("cy", function (d) { return focus.y(d.Relevance); })
console.log("BrushEnd Domain: [" + focus.x.domain()[0] + ", " + focus.x.domain()[1] + "]");
}
else {
focus.x.domain(context.x.domain());
focus.y.domain(context.y.domain());
focus.g.select(".x.axis").call(focus.xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.g.select(".y.axis").call(focus.yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
var circle = focus.g.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
.attr("cy", function (d) { return focus.y(d.Relevance); })
}
}
}
function MakeFocusZoomable(focus) {
focus.svg.call(d3.behavior.zoom().x(focus.x).y(focus.y).on("zoom", Zoom));
function Zoom() {
focus.svg.select(".x.axis").call(focus.xAxis).selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
focus.svg.select(".y.axis").call(focus.yAxis).selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.svg.selectAll("circle").attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
}
</script>
</head>
<body>
<div id="chart">
</div>
</body>
</html>