d3 topojson zoom on map with cities - d3.js

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!

Related

How to draw vertical line on mouse over displaying data with d3.js

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));
}
})

Adding axes eats up my data

I'm following the D3 tutorial but adding the axis makes half of my data disappear and I don't understand why. I thought that maybe the axis is taking up the space that's meant for the data so I added an extra 10px to the transform property, but it doesn't make any difference.
var GIST = "https://gist.githubusercontent.com/charisseysabel/f8f48fbf11b8a1b0d62cbe2d6bdc2aa6/raw/2ead1537adb822fbd59a666afd5334d525480a13/nano-2017.tsv"
var width = 1000,
height = 550,
margin = {top: 20, right: 30, bottom: 30, left: 4};
var y = d3.scaleLinear()
.range([height, 0]);
var yScale = d3.scaleLinear()
.range([height, 0]);
var xScale = d3.scaleLinear()
.range([0, width]);
var xAxis = d3.axisLeft(yScale);
var yAxis = d3.axisBottom(xScale);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
chart.append("g")
.attr("transform", "translate(10, 0)")
.call(xAxis);
chart.append("g")
.attr("transform", "translate(0, 540)")
.call(yAxis);
d3.tsv(GIST, type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + ((i * barWidth) + 10) + ",0)"; }
);
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", (barWidth / 2) - 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value;
return d;
}
When you do this...
var bar = chart.selectAll("g").etc...
... you're selecting group elements that already exist in the SVG, which are the axes, and binding your data to them.
There are two easy solutions:
Move your code that creates the axes to the bottom of the d3.tsv, that is, after you have appended the bars.
Select something that doesn't exist, like
var bar = chart.selectAll(null).etc. To read more about the logic behind selectAll(null), have a look at my answer here.

D3 V3 Multi-line Chart - Issues appending lines to svg

I'm having issues getting D3v4 to show lines on a chart. I might be getting v3/v4 syntax confused.
I have the data nested as there are 5 lines.
// Chart Canvas Dimentions
var margin = {top: 20, right: 80, bottom: 30, left: 50};
var width = 900;
var height = 600;
// Time Parse
var parseTime = d3.time.format("%Y-%m-%d %H:%M:%S");
// Chart Axis Sizes
yAxisMax = Math.max.apply(Math, data.map(function(o){return o.value;})) * 1.1;
yAxisMin = Math.min.apply(Math, data.map(function(o){return o.value;})) - (this.yAxisMax * 0.1);
xAxisMax = width * 0.99;
console.log('yAxisMax: '+yAxisMax);
console.log('yAxisMin: '+yAxisMin);
console.log('xAxisMax: '+xAxisMax);
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
chartLine = d3.svg.line()
.x(function(d){ return x(parseTime(d.date)) })
.y(function(d){ return y(d.value) })
.interpolate("basis");
// Nest Entries by Name (Groups the Lines by Names - Seperate Entities)
var nestedData = d3.nest()
.key(function(d) { return d.name; })
.entries(data);
// D3 Chart - This is the Context to Work With
var context = d3.select("#chartContainer").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "D3lineChart")
.attr("class", "D3EventScopeContainer")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Interactive HoverLine
var hoverLine = context
.append('g')
.attr('class', 'hoverLineGroup')
.append("line")
.attr('transform', 'translate(70,0)')
.attr('class', 'interactiveHoverLine hidden')
.attr("x1", 0).attr("x2", 0)
.attr("y1", 0).attr("y2", height);
// Loop through data
nestedData.forEach(function(d,i) {
console.dir(d)
console.dir(d.values)
// Add Line
context
.append('g')
.attr('class', 'lineGroup')
.append('path')
.attr('transform', 'translate(70,0)')
.attr('class', 'chartLinesGroup tag'+ d.key.replace(/\s+/g, '').replace('.', '').replace('-', '').toLowerCase())
.style("stroke", function() { return d.color = color(d.key); }) // Add the colours dynamically
.style("stroke-opacity", 1)
//.attr('d', chartLine(d.values))
.on("mouseover", function() {
d3.select(this)
.style("stroke-width", 7.5)
})
.on("mouseout", function() {
d3.select(this)
.style("stroke-width", 2.5)
});
});
It fails when I enable the line
.attr('d', chartLine(d.values))
This function must not be formated correctly to use the data.
The error I get is - related to date processing:
Any advice would be greatly appreciated.
I'm essentially trying to get the the lines to show on the chart.
thanks
*** I get around the error message by adding .parse to the end of the time format line:
// Time Parse
var parseTime = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
Still nothing showing on the screen - div/svg has height/width set...
hummmmm
You need to read API;) But at first u must try :
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom(x).tickFormat(d3.timeFormat("%H:%M:%S.%L"));
var yAxis = d3.axisLeft(y);
parseTime = d3.timeParse("%Y-%m-%d %H:%M:%S.%L");
chartLine = d3.line()
.curve(d3.curveMonotoneX)
.x(function(d){ return x(parseTime(d.date)) })
.y(function(d){ return y(d.value) });
Hope its help

color category in d3.js

histogram
I want to plot histogram using d3.js where i have dataset of around 13000 points which is divided into 2 clusters . I want to color both of them but when i use category color it only shows first one.In the input file i have Droplet_no, Amplitude, Cluster.
Here is my code :
<script type="text/javascript">
d3.csv("test_F06.csv",function type(d){
d.Droplet_no = +d.Droplet_no;
d.Amplitude = +d.Amplitude;
return d;} , function(data){
var width = 600;
height = 500;
padding = 50;
var colorColumn = "Cluster";
var map = data.map(function(i){return parseInt(i.Amplitude);})
var x = d3.scale.linear()
.domain([0, d3.max(map)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x);
var numbins = 3000;
var histogram = d3.layout.histogram()
.bins(x.ticks(numbins))
(map);
var y = d3.scale.linear()
.domain([0, d3.max(histogram.map(function(i){return i.length;}))])
.range([0, height/2]);
var colorScale = d3.scale.category10();
var canvas = d3.select("body").append("svg")
.attr("width", width+padding)
.attr("height", height+ padding)
.append("g")
.attr("transform", "translate(20,0)")
var bars = canvas.selectAll(".bar")
.data(histogram)
.enter()
.append("g")
.attr("class", "bar")
var group = canvas.append("g")
.attr("tansform","translate(0, " + height + ")")
bars.append("rect")
.attr("x", function(d){return x(d.x);})
.attr("y", function(d){return 500-y(d.y);})
.attr("width", function(d){return d.dx;})
.attr("height", function(d){ return y(d.y);})
.attr("fill", function(d){return colorScale(d[colorColumn]);});
})
</script>
Can anyone help me?
I am attaching image of the plot as well

Horizontal gradient in a bar chart

I have a bar chart.
function svg_render(data, svg) {
var node = d3.select(svg).append("svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
var y = d3.scale.linear().range([height, -height]);
var max_val = d3.max(data, function(d) {
return d;
});
y.domain([-max_val, max_val]);
var x = d3.scale.linear().domain([0, data.length]);
var bar_width = width / data.length;
var chart = node.attr("width", width).attr("height", height);
var bar = chart.selectAll("g")
.data(data)
.enter().append("g") // svg "group"
.attr("transform", function(d, i) {
return "translate(" + i * bar_width + ",0)";
});
bar.append("rect")
.attr("y", function(d) {
var yv = height - Math.abs(y(d) / 2) - height / 2 + 2;
return yv;
})
.attr("height", function(d) {
return Math.abs(y(d));
})
.attr("width", bar_width);
var axis = d3.svg.axis()
.scale(y)
.ticks(12)
.orient("left");
d3.select(".svg").append("svg")
.attr("class", "axis")
.attr("width", 60)
.attr("height", height)
.append("g")
.attr("transform", "translate(40," + (height / 2) + ")")
.call(axis);
}
would be great to be able to have a gradient towards the chart. An horizontal one.
Something like
Each bar can have a specific rgb code, but would be better if it was all calculated with a single gradient.
Also, bonus question, why i have that white lines as a border of my bars if i actually didn't specify any border at all (feels like aliasing svg issue)
So, i managed to achieve that by doing
var color = d3.scale.linear()
.domain([0, width])
.range(["hsl(62,100%,90%)", "hsl(222,30%,20%)"]);
And later on, for each bar element append
.style("fill", function(d, i) {
return color(i);
});
wonder if it's the fast way to do this

Resources