I am trying to create a tooltip in a D3 map. (also using DC.js). The problem is that it seems that the DC chart (mexicoChart) which holds the map is above everything or it is not compatible with the following code
(Nothing happens after adding it):
var width = 960
var height = 500
var projection = d3.geo.mercator()
.scale(1200)
.center([-102.34034978813841, 24.012062015793]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("svg")
.append("g")
.attr("width", width)
.attr("height", height);
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("width", 600);
var g = svg.append("g");
d3.json("static/geojson/mx_tj.json", function(error, mx) {
svg.selectAll("path")
.data(topojson.feature(mx, mx.objects.municipalities))
.enter().append("path")
.attr("d", d3.geo.path().projection(projection))
.attr("fill", "transparent")
.style("stroke", "#333")
.style("stroke-width", ".2px")
.attr("class", "muns");
g.selectAll("path")
.data(topojson.feature(mx, mx.objects.states))
.enter().append("path")
.attr("d", d3.geo.path().projection(projection))
.attr("fill", "transparent")
.style("stroke", "#333");
svg.append("g")
.attr("class", "muns")
.selectAll("path")
.data(topojson.feature(mx, mx.objects.municipalities))
.enter().append("path")
.attr("d", path)
.on("mouseover", function (d) {var tip = "<h3>" + d.properties.name + "</h3>";
tooltip.html(tip)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY) + "px");
tooltip.transition()
.duration(500)
.style("opacity", .7); })
.on("mouseout", function (d) { tooltip.transition()
.duration(500)
.style("opacity", 0);
});
});
This is the second part of the code where the DC Charts are:
queue()
.defer(d3.json, "/homicidios/projects")
.defer(d3.json, "static/geojson/municipios.json")
.await(makeGraphs);
function makeGraphs(error, projectsJson, mx) {
//Clean projectsJson data
var homicidiosProjects = projectsJson;
var dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");
homicidiosProjects.forEach(function(d) {
d["fecha"] = dateFormat.parse(d["fecha"]);
d["fecha"].setMinutes(0);
d["fecha"].setSeconds(0);
// d["no_muertos"] = +d["no_muertos"];
});
//Create a Crossfilter instance
var ndx = crossfilter(homicidiosProjects);
//Define Dimensions
var FechaDim = ndx.dimension(function(d) {
return d["fecha"];
});
var zonaDim = ndx.dimension(function(d) {
return d["entidad_federativa"];
});
var pobrezaDim = ndx.dimension(function(d) {
return d["marginacion"];
});
var MunDim = ndx.dimension(function(d) {
return d["CVE_MUN"];
});
var noMuertosDim = ndx.dimension(function(d) {
return d["no_muertos"];
});
//Calculate metrics
var numProjectsPorFecha = FechaDim.group();
var numProjectsPorZona = zonaDim.group();
var numProjectsPorPobreza = pobrezaDim.group();
var noMuertosPorMun = MunDim.group().reduceSum(function(d) {
return d["no_muertos"];
});
var all = ndx.groupAll();
var noMuertos = ndx.groupAll().reduceSum(function(d) {
return d["no_muertos"];
});
var max_mun = noMuertosPorMun.top(1)[0].value;
//Define values (to be used in charts)
var minDate = FechaDim.bottom(1)[0]["fecha"];
var maxDate = FechaDim.top(1)[0]["fecha"];
//Charts
var timeChart = dc.barChart("#time-chart");
var zonaChart = dc.rowChart("#zona-row-chart");
var pobrezaChart = dc.rowChart("#pobreza-row-chart");
var mexicoChart = dc.geoChoroplethChart("#mexico-Chart");
var numberProjectsND = dc.numberDisplay("#number-projects-nd");
var noMuertosND = dc.numberDisplay("#no-muertos-nd");
numberProjectsND
.formatNumber(d3.format("d"))
.valueAccessor(function(d) {
return d;
})
.group(all);
noMuertosND
.formatNumber(d3.format("d"))
.valueAccessor(function(d) {
return d;
})
.group(noMuertos)
.formatNumber(d3.format(".3s"));
timeChart
.width(1000)
.height(180)
.margins({
top: 10,
right: 50,
bottom: 20,
left: 50
})
.dimension(FechaDim)
.group(numProjectsPorFecha)
.transitionDuration(500)
.x(d3.time.scale().domain([minDate, maxDate]))
.elasticY(true)
.xAxisLabel("Dia")
.yAxis().ticks(10);
zonaChart
.width(300)
.height(350)
.dimension(zonaDim)
.group(numProjectsPorZona)
.xAxis().ticks(4);
pobrezaChart
.width(300)
.height(250)
.dimension(pobrezaDim)
.group(numProjectsPorPobreza)
.xAxis().ticks(4);
d3.json("static/geojson/municipios.json", function (error, mx) {
mexicoChart.width(1000)
.height(630)
.dimension(MunDim)
.group(noMuertosPorMun)
.colors(["#FFE5EC", "#FFCCD9", "#FFB2C6", "#FF99B3", "#FF7FA0", "#FF668D", "#FF4C7A", "#FF3367", "#FF1954", "#FF0041"])
.colorDomain([0, max_mun])
.overlayGeoJson(mx.features, "municipio", function(d) {
return d.properties.CVE_MUN;
})
.projection(d3.geo.mercator()
.center([-101.45, 19.28])
.scale(1600)
.translate([490, 490]))
.title(function(p) {
return "State: " + p["key"] +
"\n" +
"Total Muertos: " + Math.round(p["value"]) + " $";
})
dc.renderAll();
});
}
Related
I want to get bounding boxes for each country from a topojson, but when I add them as svg rectangles they are bundled together towards 0,0.
Ive re-read the API and played around with the order of the bound coordinates, but that didn't change anything! Also, I tried to use the SVG method getBBox() on the country paths but that produced the same result.
Any ideas?
var width = 700,
height = 400,
bboxes = [];
d3.queue()
.defer(d3.json, "data/world.topojson")
.await(ready);
//Map projection
var proj = d3.geoMercator()
.scale(100)
.center([-0.0018057527730242487, 11.258678472759552]) //projection center
.translate([width / 2, height / 2]) //translate to center the map in view
//Generate paths based on projection
var myPath = d3.geoPath().projection(proj);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
//Group for the map features
var map = svg.append("g")
.attr("class", "map");
function ready(error, geodata) {
if (error) return console.log(error); //unknown error, check the console
//Create a path for each map feature in the data
map.selectAll("path")
.data(topojson.feature(geodata, geodata.objects.subunits).features) //generate features from TopoJSON
.enter()
.append("path")
.attr("class", "country")
.attr("id", function(d) {
return d.id;
})
.attr("d", myPath);
bboxes = boundingExtent(topojson.feature(geodata, geodata.objects.subunits).features);
svg.selectAll("rect")
.data(bboxes)
.enter()
.append("rect")
.attr("id", function(d){
return d.id;
})
.attr("class", "bb")
.attr("x1", function(d) {
return d.x;
})
.attr("y1", function(d) {
return d.y;
})
.attr("width", function(d) {
return d.width;
})
.attr("height", function(d) {
return d.height;
})
}
function boundingExtent(features) {
var bounds= [];
for (var x in features) {
var boundObj = {};
thisBounds = myPath.bounds(features[x]);
boundObj.id = features[x].id;
boundObj.x = thisBounds[0][0];
boundObj.y = thisBounds[0][1];
boundObj.width = thisBounds[1][0] - thisBounds[0][0];
boundObj.height = thisBounds[1][1] - thisBounds[0][1];
boundObj.path = thisBounds;
bounds.push(boundObj)
}
return bounds;
}
function boundExtentBySvg(){
var countries = svg.selectAll(".country")
countries.each(function(d){
var box = d3.select(this).node().getBBox();
bboxes.push({id: d.id, x: box.x, y : box.y, width: box.width, height : box.height})
})
}
In these lines:
.attr("x1", function(d) {
return d.x;
})
.attr("y1", function(d) {
return d.y;
})
rect does not have an attribute of x1 or y1, I think you meant just x and y.
Here's your code running (note, I switched out the topojson file which caused slight code changes):
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<script data-require="topojson.min.js#3.0.0" data-semver="3.0.0" src="https://unpkg.com/topojson#3.0.0"></script>
</head>
<body>
<svg width="700" height="400"></svg>
<script>
var width = 700,
height = 400,
bboxes = [];
d3.queue()
.defer(d3.json, "https://unpkg.com/world-atlas#1/world/110m.json")
.await(ready);
//Map projection
var proj = d3.geoMercator()
.scale(100)
.center([-0.0018057527730242487, 11.258678472759552]) //projection center
.translate([width / 2, height / 2]) //translate to center the map in view
//Generate paths based on projection
var myPath = d3.geoPath().projection(proj);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
//Group for the map features
var map = svg.append("g")
.attr("class", "map");
function ready(error, geodata) {
if (error) return console.log(error); //unknown error, check the console
//Create a path for each map feature in the data
map.selectAll("path")
.data(topojson.feature(geodata, geodata.objects.countries).features) //generate features from TopoJSON
.enter()
.append("path")
.attr("class", "country")
.attr("id", function(d) {
return d.id;
})
.attr("d", myPath);
bboxes = boundingExtent(topojson.feature(geodata, geodata.objects.countries).features);
svg.selectAll("rect")
.data(bboxes)
.enter()
.append("rect")
.attr("id", function(d) {
return d.id;
})
.attr("class", "bb")
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.attr("width", function(d) {
return d.width;
})
.attr("height", function(d) {
return d.height;
})
.style("fill", "none")
.style("stroke", "steelblue");
}
function boundingExtent(features) {
var bounds = [];
for (var x in features) {
var boundObj = {};
thisBounds = myPath.bounds(features[x]);
boundObj.id = features[x].id;
boundObj.x = thisBounds[0][0];
boundObj.y = thisBounds[0][1];
boundObj.width = thisBounds[1][0] - thisBounds[0][0];
boundObj.height = thisBounds[1][1] - thisBounds[0][1];
boundObj.path = thisBounds;
bounds.push(boundObj)
}
console.log(bounds)
return bounds;
}
function boundExtentBySvg() {
var countries = svg.selectAll(".country")
countries.each(function(d) {
var box = d3.select(this).node().getBBox();
bboxes.push({
id: d.id,
x: box.x,
y: box.y,
width: box.width,
height: box.height
})
})
}
</script>
</body>
</html>
I have this d3js code:
var tooltip = tooltipd3();
var svg = d3.select("svg#svg-day"),
margin = {
top: 20,
right: 30,
bottom: 30,
left: 25,
padding: 15
},
width = 700 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// parse the periodo / time
var parseTime = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width - margin.padding]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = d3.area()
.x(function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.y0(height)
.y1(function(d) {
return y(d.guadagno);
});
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.y(function(d) {
return y(d.guadagno);
});
var div = d3.select("svg#svg-day")
.append("div") // declare the tooltip div
.attr("class", "tooltip") // apply the 'tooltip' class
.style("opacity", 0);
// get the data
d3.csv(base_url() + 'graph/getStatementsDaily/', function(error, data) {
if (error) throw error;
$('.graph-loading').hide();
// format the data
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
d.guadagno = +d.guadagno;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.periodo;
}));
y.domain([0, d3.max(data, function(d) {
return d.guadagno + ((d.guadagno / 100) * 10); // 10% in più sulla scala numerica
})]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.attr("cy", function(d) {
return y(d.guadagno);
})
.on('mouseover', function(d) {
var html = '<h5>' + d.guadagno + ' €</h5>';
tooltip.mouseover(html); // pass html content
})
.on('mousemove', tooltip.mousemove)
.on('mouseout', tooltip.mouseout);
// add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + (margin.left + margin.padding) + "," + (height) + ")")
//HERE IS THE DATES CODE
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")))
// add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate (" + (margin.left + margin.padding) + " 0)")
.call(d3.axisLeft(y));
});
The dates care coming from a CSV file that has this format:
periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0
The result is fine with lots of dates, but with 7 dates I get duplicates as you can see here:
Why is this?? And how do I fix it?
This is something that bothers a lot of people new to D3: the ticks in the axis, specially when using a time scale, are automatically generated. In your case, given the date interval in your domain, it coincidentally ended up creating two ticks for each day. But pay attention to this: those ticks represent different times (hours) in the same day (you can see that if you remove the tickFormat in the axis generator).
Let's see your code generating the x axis:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>
As you can see, there are two ticks for each day (remember, for different hours).
Let's show that this is a coincidence: This is the same code, but changing the last date for 2017-05-20:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-20,0.0</pre>
Back to your code.
The solution is quite simple: using intervals. Let's set the interval for each tick:
d3.axisBottom(x).ticks(d3.timeDay)
Here is the same code with that change only:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")).ticks(d3.timeDay)(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>
I am having a problem with my code where the circle nodes that I have created do not seem to be appending themselves to the graph. The debugger isn't pulling up any errors, so it seems like it's a logic issue.
Here's what is looks like:
(dot)
|
|
|
|
|
| <--(where dot should be)
|
__________________________________________
^
|
graph
And here's my code:
<script>
var slider = $("#myRange").val();;
var xtwo;
var xone;
var xzero;
var firstx = -1;
var firsty = 1;
var secondx = 2;
var secondy = 4;
var lineData = [];
var nodes = [];
//updates coefficients
function updateXs() {
xtwo = (75 - slider) / 50;
xone = (slider - 25) / 50;
xzero = (slider - 25) / 25;
}
//gets corresponding y from x and coefficients
function getY(xval) {
return (xval * xval * xtwo + xval * xone + xzero);
}
function displayVals() {
document.getElementById("demo").innerHTML = slider + " " + xtwo + " " + xone + " " + xzero + " " + lineData;
}
function updateLineData() {
//resets and fills points
lineData = [];
for (i = (firstx - 1); i < (secondx + 2); i++) {
lineData.push({
x: i,
y: getY(i)
});
}
}
//makes dots for static points
function makeDots(xvalue, xvalue2) {
nodes = [{
x: xvalue,
y: getY(xvalue)
}, {
x: xvalue2,
y: getY(xvalue2)
}]
}
function makeLine() {}
$(document).ready(function() {
updateXs();
updateLineData();
displayVals();
var vis = d3.select('#visual'),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 50
},
xRange = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(lineData, function(d) {
return d.x;
}), d3.max(lineData, function(d) {
return d.x;
})]),
yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(lineData, function(d) {
return d.y;
}), d3.max(lineData, function(d) {
return d.y;
})]),
xAxis = d3.svg.axis()
.scale(xRange)
.tickSize(5)
.tickSubdivide(true),
yAxis = d3.svg.axis()
.scale(yRange)
.tickSize(5)
.orient("left")
.tickSubdivide(true);
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineFunc = d3.svg.line()
.x(function(d) {
return xRange(d.x);
})
.y(function(d) {
return yRange(d.y);
})
.interpolate('basis');
vis.append("svg:path")
.attr("d", lineFunc(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
makeDots(firstx, secondx);
//puts in dots
vis.selectAll("circle.nodes")
.data(nodes)
.enter()
.append("circle")
.attr("cx", function(d) {
console.log(d.x)
return 200;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", "10px")
.attr("fill", "black")
//updates when slider changes
$("#myRange").change(function() {
slider = $("#myRange").val();
updateXs();
updateLineData();
displayVals();
});
});
</script>
Please help and thanks in advance. Also, I was wondering if I could get some tips on how to make the line move using the .change function
You haven't applied your scale function to your dots:
vis.selectAll(".nodes")
.data(nodes)
.enter().append("circle")
.attr("class", "nodes")
.attr("cx", function (d) {
return xRange(d.x); //<-- convert from user space to pixel space
})
.attr("cy", function (d) {
return yRange(d.y); //<-- convert from user space to pixel space
});
To make your line update, you need to select it and change the d attribute:
d3.select(".myLine") //<-- select it by some unique class
.attr("d", lineFunc(lineData)); //<-- update the d attribute
To get a smooth curve without interpolation, just supply more points. See udpated fiddle.
Fiddle here.
The lines of my chart are drawing off my chart. I've tried to replace this code:
yE.domain(d3.extent(data, function(E) { return E.close;}));
With this:
yE.domain([0,d3.max(data, function(E) {
return Math.max(E.close, E.Map1, EMap2, E.MapII);
})]);
Based on the answer from Bill: d3.js: dataset array w/ multiple y-axis values
Mine doesn't work.
My entire code:
var marginE = {top: 30, right: 20, bottom: 30, left: 50},
widthE = 400 - marginE.left - marginE.right,
heightE = 270 - marginE.top - marginE.bottom;
// Parse the date / time
var parseDateTimeE = d3.time.format("%Y-%m-%d%H:%M").parse;
// Set the ranges
var xE = d3.time.scale().range([0, widthE]);
var yE = d3.scale.linear().range([heightE, 0]);
// Define the axEs
var xAxisE = d3.svg.axis().scale(xE)
.orient("bottom").ticks(6);
var yAxisE = d3.svg.axis().scale(yE)
.orient("left").ticks(6);
var areaE = d3.svg.area()
.interpolate("bundle")
.x(function(e) { return xE(e.date); })
.y0(heightE)
.y1(function(e) { return yE(e.close); });
// Adds the svg canvas
var svgE = d3.select(".eur")
.append("svg")
.attr("width", widthE + marginE.left + marginE.right)
.attr("height", heightE + marginE.top + marginE.bottom)
.attr('id', 'charteur')
.attr('viewBox', '0 0 400 270')
.attr('perserveAspectRatio', 'xMinYMid')
.append("g")
.attr("transform",'translate(' + marginE.left + ',' + marginE.top + ')')
.attr('width', widthE)
.attr('height', heightE)
.style("font-size","12px");
// Get the data
d3.json("php/downl_EUR.php", function(error, data) {
data.forEach(function(E) {
E.date = parseDateTimeE(E.date +E.time);
E.close = +E.close;
E.MaP1 = +E.MaP1;
E.MaP2 = +E.MaP2;
E.MaPII = +E.MaPII; });
// Define the line
var valuelineE = d3.svg.line()
.interpolate("bundle")
.x(function(E) { return xE(E.date); })
.y(function(E) { return yE(E.close); });
var valuelineE2 = d3.svg.line()
.interpolate("bundle")
.x(function(E) { return xE(E.date); })
.y(function(E) { return yE(E.MaP1); });
var valuelineE3 = d3.svg.line()
.interpolate("bundle")
.x(function(E) { return xE(E.date); })
.y(function(E) { return yE(E.MaP2); });
var valuelineE4 = d3.svg.line()
.interpolate("bundle")
.x(function(E) { return xE(E.date); })
.y(function(E) { return yE(E.MaPII); });
// Scale the range of the data
xE.domain(d3.extent(data, function(E) { return E.date; }));
yE.domain(d3.extent(data, function(E) { return E.close;})); //****
//yE.domain([0,d3.max(data, function(E) {return Math.max(E.close, E.MapII);})]); ****
svgE.append("path")
.datum(data)
.attr("class", "area")
.attr("d", areaE);
// Add the valueline path.
svgE.append("path")
.attr("class", "lineE")
.style("stroke", "steelblue")
.attr("d", valuelineE(data));
svgE.append("path")
.attr("class", "lineE")
.style("stroke", "red")
.attr("d", valuelineE2(data));
svgE.append("path")
.attr("class", "lineE")
.style("stroke", "green")
.attr("d", valuelineE3(data));
svgE.append("path")
.attr("class", "lineE")
.style("stroke-dasharray", ("3, 3"))
.attr("d", valuelineE4(data));
// Add the X Axis
svgE.append("g")
.attr("class", "XaxisE")
.attr("transform", "translate(0," + heightE + ")")
.call(xAxisE);
// Add the Y Axis
svgE.append("g")
.attr("class", "YaxisE")
.call(yAxisE); });
I found the solution myself. Instead of MaP1, Map2 & MaPII, use map1, map2 and mapii -> no capital letters !! (also in the php-file)
Everything works fine now...
I'm populating a graph with multiple lines and trying to access the highest values, then incrementing them every few seconds. I'm not sure what syntax to use to access each line separately...
index.html:
function generateVisualization(data){
//className for first timeline
var className = "ch1Class";
data.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price; // + ensures its an integer
});
maxY = d3.max(data.map(function(d) { return d.price; }));
obj[className] = maxY;
maxX = d3.max(data.map(function(d) { return d.date; }));
objD[className] = maxX;
x.domain(d3.extent(data.map(function(d) { return Math.max(d.date); })));
y.domain([0, d3.max(data.map(function(d) { return Math.max(d.price); }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("clip-path", "url(#clip)")
.attr("d", area)
.attr("class", className);
//make sure the first textbox is selected in the channel selection
$("#demo_box_1").attr("checked","checked");
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", className)
.attr("d", area2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
}
<!-- time series start -->
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
//var parseDate = d3.time.format("%x %X.%L").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var area = d3.svg.area()
.interpolate("monotone")//smooths path corners
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var area2 = d3.svg.area()
.interpolate("monotone")//smooths path corners
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
var svg = d3.select("#maincontent1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "timeLine");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var dataset; //global variable
d3.csv("ch1.csv", function(error, data) {
dataset = data;
generateVisualization(data);
});
<!-- time series end -->
</script>
</div><!-- end #maincontent -->
included js file:
//create an object to track max y values
var obj = {};
var objD = {};
// if adding/removing paths check max values and rescale if necessary
function maxValue(maxY,maxX,addRemove,className){
//if adding and its not in the array yet, then add to array, then check to see if its got the highest yvalue, if so updateaxis
if(addRemove=="add"){
if((className in obj )!=true){
// add new class/max value to an object
obj[className] = maxY;
objD[className] = maxY;
//check to see if its the highest yvalue in the object and if so, rescale
var max = 0;
for(var i in obj){
if(obj[i] > max) {
max = obj[i];
}
}
if (max==obj[className]){
updateAxis("y");
//reScaleEverything();
}
// important, reset the y domain to the new max, if you don't do this then the brushing will use the last added paths y max
y.domain([0, max]);
}
} else if (addRemove=="remove"){
//if its being remove then it should already have a matching obj, remove it from the obj array and then test for the new max yVal, rescale to new max
if((className in obj )==true){
var max = 0;
for(var i in obj){
if(obj[i] > max) {
max = obj[i];
}
}
if (max==obj[className]){
delete obj[className];
delete objD[className];
var max = 0;
for(var i in obj){
if(obj[i] > max) {
max = obj[i];
}
}
//set the new y domain
y.domain([0, max]);
//y2.domain([0, max]);
//rescale y using the new max value
updateAxis("y");
}
}
}
}
// found in maxValue(), this is called after setting domains
function updateAxis(axisXY){
if (axisXY == 'x'){//Update x-axis
svg.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
} else if (axisXY == 'y'){
//Update y-axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
}
// very important, rescales all existing paths to new y values
focus.selectAll("path").attr("d", area);
//focus.selectAll("path").attr("d", area2);
//focus.select(".x.axis").call(xAxis);
}
function rescaleEverything(){
}
function checkSetY(maxY){
var max = 0;
for(var i in obj){
if(obj[i] > max) {
max = obj[i];
}
}
if(maxY>max){
//if the new maxY is bigger then any other Y then set a new domain
return y.domain([0, maxY]);
}
}
function checkSetX(maxX){
console.log(maxX);
var max = 0;
for(var i in objD){
if(objD[i] > max) {
max = objD[i];
}
}
if(maxX>max){
//if the new maxX is bigger then any other Y then set a new domain
return x.domain([0, maxX]);
}
}
function addCSV(csvFileName, className){
d3.csv(csvFileName, function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
maxY = d3.max(data.map(function(d) { return d.price; }));
checkSetY(maxY);
maxX = d3.max(data.map(function(d) { return d.date; }));
checkSetX(maxX);
//x.domain(d3.extent(data.map(function(d) { return d.date; })));
//y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("clip-path", "url(#clip)")
.attr("d", area)
.attr("class", className);
context.append("path")
.datum(data)
.attr("class", className)
.attr("d", area2);
// important, this fixes brushing issues, avoids multiple context brush areas
context.selectAll("g.x.brush").remove();
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
maxValue(maxY,maxX,"add",className);
//maxX = d3.max(data.map(function(d) { return Date.max(d.date); }));
//console.log(maxX);
});
}
function removeCSV(className){
var classRemoval = "." + className;
d3.selectAll(classRemoval).transition().remove();
maxValue(maxY,maxX,"remove",className);
}
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
//console.log(x2);
focus.selectAll("path").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
function incrementDate(){
//pass in last date
}
$(document).ready(function() {
//addCSV("sp501.csv", "timeLine2");
//toggle the side panel sliding
$(".togglePanel").click(function(){
//$("#channels").animate({left:'-130px'},350);
var $lefty = $("#channels");
$lefty.animate({
left: parseInt($lefty.css('left'),10) == 0 ?
-$lefty.outerWidth() :
0
});
});
$("#maincontent2, #maincontent3").hide();
$(".gauge").hover(
function(){
var idName = $(this).attr('id');
var newName = "." + idName.substring(0,3) + "Class";
//alert(newName);
$(newName).css("fill","#c2c2c2");
$(newName).css("fill-opacity",".7");
//$(newName).css("stroke-width","3");
},
function(){
var idName = $(this).attr('id');
var newName = "." + idName.substring(0,3) + "Class";
$(newName).css("fill","none");
$(newName).css("stroke-width","1");
}
);
//hide all gauges
$(".gauge").hide();
//this one starts already open
$("#ch1GaugeContainer").slideDown();
$("#tabs li").click(function() {
$("#tabs li").removeClass("selected");
$(this).addClass("selected");
});
$('#channelSelect :checkbox').click(function() {
var $this = $(this);
var channelName = $this.attr("value") + ".csv";
var className = $this.attr("value") + "Class";
var guageName = "#" + $this.attr("value") + "GaugeContainer";
// $this will contain a reference to the checkbox
if ($this.is(':checked')) {
addCSV(channelName,className);
$(guageName).slideDown();
} else {
removeCSV(className);
$(guageName).slideUp();
}
});
});