The problem i am facing is that i am not able to write the function following in my code because of version mismatch.
Code is
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
I have tried by this way
const zoom = d3.zoom()
.scaleExtent([1, 4])
.x(this.xScale)
.on('zoom', () => {})
But it does not work for me.
How to write same function in d3 version 5? I want to make line chart scrollable in x axis with y axis as fixed position using d3 version 5
This is my implementation Basic Code
private createLineChart() {
this.width = 2000 - this.margin.left - this.margin.right;
this.height = 600 - this.margin.top - this.margin.bottom;
// X AXIS
this.xScale = d3.scaleBand()
.domain(this.dataset[0].fluencyData.map((data) => {
return new Date(data.date);
}))
.range([0, this.width]);
// Y AXIS
this.yScale = d3.scaleLinear()
.domain([0, 110])
.range([this.height, 0]);
// Line Generator
this.line = d3.line()
.x((data) => this.xScale(new Date(data.date)))
.y((data) => this.yScale(data.wcpm));
// .curve(d3.curveMonotoneX);
// Add SVG to Div
this.svg = d3.select('#displayChart').append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr(
'viewBox',
'0 0 ' +
(this.width + this.margin.left + this.margin.right) +
' ' +
(this.height + this.margin.top + this.margin.bottom))
// .attr('width', this.width + this.margin.left + this.margin.right)
// .attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
// Define the div for the tooltip
this.toolTipDiv = d3.select('#displayChart').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
// Append XAXIS to the SVG
this.svg.append('g')
.attr('class', 'xAxis')
.attr('transform', 'translate(0,' + this.height + ')')
.call(d3.axisBottom(this.xScale).tickSizeOuter(0).tickFormat(d3.timeFormat('%b %d')));
const zoom = d3.zoom()
.scaleExtent([1, 4])
.extent([100, 100], [this.width - 100, this.height - 100])
.x(this.xScale)
.on('zoom', () => {
console.log(d3.event.transform);
// this.svg.select('#displayChart').attr('d', this.line);
});
this.svg.call(zoom);
// Append YAXIS to SVG
this.svg.append('g')
.attr('class', 'yAxis')
.call(d3.axisLeft(this.yScale).tickSize(-this.width)
);
// Make a Path for Dataset
this.svg.append('path')
.datum(this.dataset[0].fluencyData)
.attr('class', 'line')
.attr('d', this.line)
.attr('transform', 'translate(' + this.margin.left + ',0)');
// Text Heading of DATE in chart
this.svg.append('text')
.attr('transform', 'translate(' + (-20) + ',' + (this.height + 13) + ')')
.attr('dy', '.35em')
.attr('class', ' xAxis')
.text('Date');
}
}
Error I am getting is
LineChartComponent_Host.ngfactory.js? [sm]:1 ERROR TypeError: d3__WEBPACK_IMPORTED_MODULE_2__.zoom(...).scaleExtent(...).x is not a function
at LineChartComponent.push../src/app/line-chart/line-chart.component.ts
With d3v3 and before, the zoom could track a scale's state. From the documentation, scale.x(): "Specifies an x-scale whose domain should be automatically adjusted when zooming." (docs). This modifies the original scale.
D3v4+ does not have zoom.x or zoom.y methods.
With d3v4+, the zoom does not track or modifiy a d3 scale's state. Infact, for d3v4+, the zoom behavior doesn't even track the current zoom state: "Zoom behaviors no longer store the active zoom transform (i.e., the visible region; the scale and translate) internally. The zoom transform is now stored on any elements to which the zoom behavior has been applied.(change log)".
As part of this, and more importantly, "Zoom behaviors are no longer dependent on scales, but you can use transform.rescaleX, transform.rescaleY, transform.invertX or transform.invertY to transform a scale’s domain(change log)".
So rather than have the zoom update the d3 scale, we need to do this ourselves. The most common way this is done is through a reference scale, which remains unchanged, and a scale to which we apply the zoom transform:
var zoom = d3.zoom()
.on("zoom",zoomed)
var x = d3.scaleLinear().... // working scale
var x2 = x.copy(); // reference scale.
function zoomed() {
x = d3.event.transform.rescaleX(x2) // update the working scale.
// do something...
}
So, something like this:
var x = d3.scale.linear()
.domain([0,1])
.range([0,500]);
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("svg")
.call(zoom);
var axis = d3.svg.axis()
.orient("bottom")
.scale(x);
var axisG = svg.append("g")
.attr("transform", "translate(0,30)")
.call(axis);
function zoomed() {
axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<svg width="500" height="200"></svg>
Becomes something like that:
var x = d3.scaleLinear()
.domain([0,1])
.range([0,500]);
var x2 = x.copy(); // reference.
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("svg")
.call(zoom);
var axis = d3.axisBottom().scale(x)
var axisG = svg.append("g")
.attr("transform", "translate(0,30)")
.call(axis);
function zoomed() {
x = d3.event.transform.rescaleX(x2)
axis.scale(x);
axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="500" height="200"></svg>
Note that d3.event.transform.rescaleX is for continuous scales - you have an ordinal band scale, so we'll need to use a slightly modified approach for band and/or point scales:
var x = d3.scaleBand()
.domain(d3.range(10).map(function(d) { return d/10; }))
.range([0,500]);
var x2 = x.copy(); // reference.
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("svg")
.call(zoom);
var axis = d3.axisBottom().scale(x)
var axisG = svg.append("g")
.attr("transform", "translate(0,30)")
.call(axis);
function zoomed() {
// Rescale the range of x using the reference range of x2.
x.range(x2.range().map(function(d) {
return d3.event.transform.applyX(d);
}))
axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="500" height="200"></svg>
This is band/point scale solution is based on this issue and Bostock's proposed solution to it
How to append a vertical line to a graph and display on a tooltip the data focused?
Something like this:
TASK:
Add line indicator and tooltip
Append an invisible div to the vis container, set its class to "tooltip" and use the index.css to define necessary styles (e.g. position)
Append an indicator line to the viewport
Append a rectangle and set its class to "interaction-rect" (see index.css). We will use this rectangle to capture the mouse-events
Whenever there is a mousemove, update the tooltip to show the correct dates and values
Whenever the mouse leaves the viewport, make the indicator and tooltip disappear
CODE:
/* Retrieve the node of the div element declared within the index.html by its identifier */
var visContainerNode = d3.select("#vis-container");
// Specify margins such that the visualization is clearly visible and no elements are invisible due to the svg border
var margins = {
top: 20,
right: 25,
bottom: 20,
left: 50
};
// Specify the width and height of the svg as well as the width height of the viewport of the visualization.
var width = 1200;
var height = 800;
var gapY = 50;
var focusAreaHeight = 600 - margins.top;
var contextAreaHeight = 200 - margins.bottom - gapY;
var visWidth = width - margins.left - margins.right;
var visHeight = focusAreaHeight + contextAreaHeight;
/* Appending an svg element to the vis-container, set its width and height (in pixels), and add it to the vis-container */
var svg = visContainerNode.append("svg").attr("width", width).attr("height", height);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", visWidth)
.attr("height", visHeight);
// Adding a group element to the svg to realize the margin by translating the group.
var viewport = svg.append("g").attr("transform", "translate(" + margins.left + "," + margins.top + ")");
var dateParser = d3.timeParse('%m %Y');
var dateFormat = d3.timeFormat('%m / %Y');
var curve = d3.curveMonotoneX;
// We use the d3.dsv method, which uses the fetchAPI internally, to retrieve the data
d3.dsv(";", "pr_1991_2015.csv", function (d) {
return {
date: dateParser(d.Month + " " + d.Year),
rain: parseFloat(d.pr),
temperature: parseFloat(d.tas)
};
}).then(function (data) {
console.log("Raw Data:", data);
// Init Scales
var xFocus = d3.scaleTime().domain(d3.extent(data, function (d) {
return d.date;
})).range([0, visWidth]);
var yRainFocus = d3.scaleLinear().domain([0, d3.max(data.map(function (d) {
return d.rain
}))]).range([focusAreaHeight, 0]);
var yTempFocus = d3.scaleLinear().domain(d3.extent(data.map(function (d) {
return d.temperature
}))).range([focusAreaHeight, 0]);
// In order to organize our code, we add one group for the focus visualization (the large lien chart)
var focusVis = viewport.append("g");
// Initialize a line generator for each line
var rainLine = d3.line()
.x(function (d) {
return xFocus(d.date);
})
.y(function (d) {
return yRainFocus(d.rain);
})
.curve(curve);
var tempLine = d3.line()
.x(function (d) {
return xFocus(d.date);
})
.y(function (d) {
return yTempFocus(d.temperature);
})
.curve(curve);
// Append two path elements
focusVis.append("path")
.datum(data)
.attr("class", "line line-rain")
.attr("d", rainLine);
focusVis.append("path")
.datum(data)
.attr("class", "line line-temp")
.attr("d", tempLine);
// Lets add some axis
var axisG = focusVis.append("g");
var xAxisFocus = d3.axisBottom(xFocus);
axisG.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + focusAreaHeight + ")")
.call(xAxisFocus);
axisG.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yTempFocus));
axisG.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + visWidth + ", 0)")
.call(d3.axisRight(yRainFocus));
// Append three text elements to the axisG group and label the axes respectively
axisG.append("text").text("Temperature").attr("x", -50).attr("y", -5).attr("fill", "red");
axisG.append("text").text("Rain").attr("x", visWidth - 10).attr("y", -5).attr("fill", "blue");
axisG.append("text").text("Years").attr("x", visWidth / 2).attr("y", focusAreaHeight - 10);
// Create the context visualization (small line chart) directly below the focus vis
// Init scales since range differs
var xContext = d3.scaleTime().domain(d3.extent(data, function (d) {
return d.date;
})).range([0, visWidth]);
var yContextRain = d3.scaleLinear().domain([0, d3.max(data.map(function (d) {
return d.rain
}))]).range([contextAreaHeight, 0]);
var yContexttemp = d3.scaleLinear().domain(d3.extent(data.map(function (d) {
return d.temperature
}))).range([contextAreaHeight, 0]);
// To organize our code, we add one group for the context visualization
var contextVis = viewport.append("g").attr("transform", "translate(0," + (focusAreaHeight + gapY) + ")");
var xAxisContext = d3.axisBottom(xContext);
contextVis.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + contextAreaHeight + ")")
.call(xAxisContext);
// Init two line generators
var rainLineContext = d3.line()
.x(function (d) {
return xContext(d.date);
})
.y(function (d) {
return yContextRain(d.rain);
})
.curve(curve);
var tempLineContext = d3.line()
.x(function (d) {
return xContext(d.date);
})
.y(function (d) {
return yContexttemp(d.temperature);
})
.curve(curve);
// Add the two lines for rain and temperature
contextVis.append("path")
.datum(data)
.attr("class", "line line-rain")
.attr("d", rainLineContext);
contextVis.append("path")
.datum(data)
.attr("class", "line line-temp")
.attr("d", tempLineContext);
/*
* Add Interactive Features here
*/
/*
TASK: Add the brush using the d3.brush function, define the extent and the necessary event functions
Append a new group element and apply the brush on it using the "call" function
During the brush and on brush end you want to make sure that the lines are redrawn correctly by setting their "d" attribute
*/
//
var brush = d3.brushX()
.extent([[-10, -10], [width+10, height+10]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
contextVis.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, xContext.range());
function brushed() {
if (d3.event || d3.event.selection)
var s = d3.event.selection || xContext.range();
xFocus.domain(s.map(xContext.invert, xContext));
focusVis.select(".line-rain").attr("d", rainLine);
focusVis.select(".line-temp").attr("d", tempLine);
focusVis.select(".x axis").call(xAxisFocus);
focusVis.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(visWidth / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event || d3.event.selection)
var t = d3.event.transform;
xFocus.domain(t.rescaleX(xContext).domain());
focusVis.select(".line-rain").attr("d", rainLine);
focusVis.select(".line-temp").attr("d", tempLine);
axisG.select(".x axis").call(xAxisFocus);
contextVis.select(".brush").call(brush.move, xContext.range().map(t.invertX, t));
}
})
Using d3.js I want to make a chart showing each item as pie chart displaying the quarterly sale of tickets for different programs
here is the sample picture of chart I want to make,each segment in the chart represents ticket for a program and its percentage of sales in each quarter. Now using this link enter link description here
I have made a graph but which is not exactly the one I needed.Is there any charts available in d3.js to show a graph as I mentioned in the picture or we need to customize it to get a graph like that.
Is there any charts available in d3.js to show a graph as I mentioned
in the picture or we need to customize it to get a graph like that?
No there isn't a ready made solution, d3 as the comment on the question notes is a collection of methods for manipulating the DOM, this allows a great deal of flexibility in creating custom visualizations (users aren't as limited as with many ready-made solutions that only allow defined modifications). Consequently, yes, you can make a chart like that in d3 taking elements and ideas from both scatter plot and pie chart implementations with d3 to make your chart.
This answer shows one approach that could be used in creating such a graph. Ideally it can provide ideas in crafting your own visualization that meets your need.
First, you need a mechanism to make variable sized pie charts and to place them - arguably this is the hardest part (after that you just have a scatter plot that's easier to manipulate). This requires some thought as to data structure, I've used a structure such as:
var data = [
{x:100,y:100,radius:20,slices:[1,5]},
{x:150,y:180,radius:10,slices:[1,2,3,4]},
You could add other properties as needed, all that this does is specify an x and y coordinate for the pie chart center, a radius for the pie chart, and the values of the wedges for each pie chart.
With that, you can append a group element (g) to your svg, one for each pie chart (or item in the data array) using a standard enter cycle in d3, positioning the groups as we go:
var pies = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.property("radius",function(d) { return d.radius; })
.attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
Because the data array used to append the wedges themselves will only include the wedge values, we can save the radius property as a property of the group and access that when appending the wedges:
pies.selectAll()
.data(function(d){ return pie(d.slices); })
.enter()
.append("path")
.attr("d",function(d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d) })
.attr("fill",function(d,i){
return color[i];
});
A basic example might look like this:
var data = [
{x:100,y:100,radius:20,slices:[1,5]},
{x:150,y:180,radius:10,slices:[1,2,3,4]},
{x:180,y:130,radius:30,slices:[1,2,3,4,5,6,7]},
{x:50,y:50,radius:15,slices:[5,3]},
{x:50,y:180,radius:40,slices:[6,3]}
]
var width = 500;
var height = 300;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var arc = d3.arc()
.innerRadius(0)
.outerRadius(50);
var pie = d3.pie()
.sort(null)
.value(function(d) { return d; });
var color = d3.schemeCategory10;
// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.property("radius",function(d) { return d.radius; })
.attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
// draw each pie wedge, using the slices property of the data bound to the parent g
pies.selectAll()
.data(function(d){ return pie(d.slices); })
.enter()
.append("path")
.attr("d",function(d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d) })
.attr("fill",function(d,i){
return color[i];
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
If you want to set each circle to have it's own color scheme, a few options might be available. If every pie has only two colors, you could assign a fill to the parent group and use the wedge increment to set transparency, creating lighter wedges such as in your image:
var data = [
{x:100,y:100,radius:20,slices:[1,5]},
{x:150,y:180,radius:10,slices:[1,2]},
{x:180,y:130,radius:30,slices:[1,7]},
{x:50,y:50,radius:15,slices:[5,3]}
]
var width = 500;
var height = 300;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var arc = d3.arc()
.innerRadius(0)
.outerRadius(50);
var pie = d3.pie()
.sort(null)
.value(function(d) { return d; });
var color = ["steelblue","orange","pink","crimson"]
// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.property("radius",function(d) { return d.radius; })
.attr("fill",function(d,i) { return color[i] })
.attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
// draw each pie wedge, using the slices property of the data bound to the parent g
pies.selectAll()
.data(function(d){ return pie(d.slices); })
.enter()
.append("path")
.attr("d",function(d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d) })
.attr("opacity",function(d,i){
return 1-i*0.2;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Other options are available, such as storing a local variable, storing the color as a property as we did for radius, or modifying our data structure to include a color for each wedge:
var data = [
{x:100,y:100,radius:20,
slices:[{value:1,color:"steelblue"},{value:5,color:"lightblue"} ]},
{x:150,y:180,radius:10,
slices:[{value:1,color:"crimson"},{value:2,color:"pink"}]},
{x:180,y:130,radius:30,
slices:[{value:1,color:"lawngreen"},{value:7,color:"darkgreen"}]}
]
var width = 500;
var height = 300;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var arc = d3.arc()
.innerRadius(0)
.outerRadius(50);
var pie = d3.pie()
.sort(null)
.value(function(d) { return d.value; });
// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.property("radius",function(d) { return d.radius; })
.attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
// draw each pie wedge, using the slices property of the data bound to the parent g
pies.selectAll()
.data(function(d){ return pie(d.slices); })
.enter()
.append("path")
.attr("d",function(d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d) })
// remember that d3.pie creates it's own data array, thus using d.data.property:
.attr("fill",function(d){ return d.data.color; })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Now we can adapt and implement characteristics of a scatter plot such as scales and axes. This would be the same for any other scatter plot essentially, we would scale the max and min (or a defined range) for the x and y scales, and add the axes. Altogether, that might look something like:
var data = [
{x:100,y:100,radius:10,slices:[1,5]},
{x:150,y:180,radius:10,slices:[1,2,3,4]},
{x:180,y:110,radius:30,slices:[1,2,3,4,5,6,7]},
{x:50,y:100,radius:15,slices:[5,3]},
{x:50,y:180,radius:40,slices:[6,3]}
]
var width = 500;
var height = 300;
var margin = {left:30,right:10,top:30,bottom:30}
var xScale = d3.scaleLinear()
.range([0,width-margin.left-margin.right])
.domain([0,d3.max(data,function(d) { return d.x + 20 }) ]);
var yScale = d3.scaleLinear()
.range([height-margin.top-margin.bottom,0])
.domain([0,d3.max(data,function(d) { return d.y + 20}) ]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")")
var xAxis = d3.axisBottom(xScale);
g.append("g")
.attr("transform", "translate(0,"+(height-margin.bottom-margin.top)+")")
.call(xAxis);
var yAxis = d3.axisLeft(yScale);
g.append("g")
.call(yAxis);
var arc = d3.arc()
.innerRadius(0)
.outerRadius(50);
var pie = d3.pie()
.sort(null)
.value(function(d) { return d; });
var color = d3.schemeCategory10;
var pies = g.selectAll(null)
.data(data)
.enter()
.append("g")
.property("radius",function(d) { return d.radius; })
.attr("transform",function(d) { return "translate("+xScale(d.x)+","+yScale(d.y)+")"; });
pies.selectAll()
.data(function(d){ return pie(d.slices); })
.enter()
.append("path")
.attr("d",function(d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d) })
.attr("fill",function(d,i){
return color[i];
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Adding grid lines, legends, mouse over functionality, and other features should be relatively straightforward now - look at scatterplot examples with d3 to see how these and other features might be implemented, modifying a scatterplot of cirlces is about the same as modifying a scatterplot of pie charts.
From the sample provided by #Andrew Reid I have made it , the sample code for reference is posted here
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
</head>
<body>
<script>
var data = [
{x: 170, y: 160, radius: 20, slices: [3, 4]},
{x: 180, y: 40, radius: 30, slices: [ 6, 7]},
{x: 50, y: 80, radius: 20, slices: [5, 3]},
{x: 50, y: 180, radius: 40, slices: [6, 3]}
]
var width = 500;
var height = 300;
var margin = {left: 30, right: 10, top: 30, bottom: 30}
var xScale = d3.scaleLinear()
.range([0, width - margin.left - margin.right])
.domain([0, d3.max(data, function (d) {
return d.x + 20
})]);
var yScale = d3.scaleLinear()
.range([height - margin.top - margin.bottom, 0])
.domain([0, d3.max(data, function (d) {
return d.y + 20
})]);
xMid=d3.max(xScale.domain())/2;
yMid=d3.max(yScale.domain())/2;
console.log(xMid,yMid)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
var xAxis = d3.axisBottom(xScale);
g.append("g")
.attr("transform", "translate(0," + (height - margin.bottom - margin.top) + ")")
.call(xAxis);
var yAxis = d3.axisLeft(yScale);
g.append("g")
.call(yAxis);
var lineX= g.append("line")
.attr("x1", 0)
.attr("x2", 500)
.attr("y1", yMid+20)
.attr("y2", yMid+20)
.attr("stroke-width", 1)
.attr("stroke", "black")
.attr("stroke-dasharray", "7,7");
var liney= g.append("line")
.attr("x1", xMid+130)
.attr("x2", xMid+130)
.attr("y1", -10)
.attr("y2", 245)
.attr("stroke-width", 1)
.attr("stroke", "black")
.attr("stroke-dasharray", "7,7");
var arc = d3.arc()
.innerRadius(0)
.outerRadius(50);
var pie = d3.pie()
.sort(null)
.value(function (d) {
return d;
});
var colors = d3.schemeCategory20;
var color = ["steelblue","orange","green","red"]
var pies = g.selectAll(null)
.data(data)
.enter()
.append("g")
.property("radius", function (d) {
return d.radius;
})
.attr("transform", function (d) {
return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
})
.attr("fill", function (d, i) {
return color[i];
});
pies.selectAll()
.data(function (d) {
return pie(d.slices);
})
.enter()
.append("path")
.attr("d", function (d) {
var radius = d3.select(this.parentNode).property("radius");
arc.outerRadius(radius);
return arc(d)
})
.attr("opacity",function(d,i){ return 1-i*0.7; });
</script>
</body>
I'm working on a d3 map with topojson format data.
I can draw the country shapes and the zoom and pane works fine.
The problem is when I try to plot cities on the map.
I can not figure out how to manage the zoom with those points: the point size must be the same, but the points must translate right.
this is an example, when I zoom the map, the points translate out of the map:
var width = 724;
var height = 768;
var objMap = null;
var x, y;
//Projection
projection = d3.geo.transverseMercator()
.center([2.5, -38.5])
.rotate([66, 0])
.scale((height * 56.5) / 33)
.translate([(width / 2), (height / 2)]);
//Path
path = d3.geo.path()
.projection(projection);
x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
y = d3.scale.linear()
.domain([0, height])
.range([height, 0]);
svg = d3.select("#div_map").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
g = svg.append("g");
// Zoom behavior
var zoom = d3.behavior.zoom()
.scaleExtent([1,15])
.on("zoom",function() {
g.selectAll("path.zoomable").attr("transform","translate("+d3.event.translate.join(",")+")scale("+d3.event.scale+")")
g.selectAll(".place").attr("transform", function(d) { p = projection(d.geometry.coordinates); return "translate(" + x(p[0]) + "," + y(p[1]) + ")"; });
}
);
svg.call(zoom);
d3.json("datos/ARcompleto.json.txt", function (error, ar) {
objMap = ar;
//Draw the map
provs = g.append("g")
.attr("id", "g_provincias")
.selectAll("path")
.data(topojson.feature(ar, ar.objects.provincias).features)
.enter().append("path")
.classed("zoomable", true)
.attr("d", path)
g.append("g")
.attr("id", "g_localidades")
.selectAll("path")
.data(topojson.feature(objMap, objMap.objects.localidades).features.filter(function (d) { return d.properties.LPROVINCIA == 'MENDOZA'; }))
.enter().append("path")
.attr("d", path)
.attr("class", "localidad")
.classed("place", true)
//.attr("transform", function(d) {return "translate(" + projection(d.geometry.coordinates.reverse()) + ")"; });
});
I can manage it with the Lars help. this is the new zoom function:
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scaleExtent([height, Infinity])
.scale(projection.scale())
.on("zoom", function() {
projection.translate(d3.event.translate).scale(d3.event.scale)
g.selectAll("path.zoomable").attr("d", path);
projection.translate(d3.event.translate).scale(d3.event.scale)
svg.selectAll(".place").attr("d", path);
});
Thanks a lot!