d3PlotEspecieIndicador = function (recife, species, indicador) {
let [w,h,hTime] = [width-250,164,50];
let margin = {left:40, right:40, top:0, bottom: 30, bottomTime: 20};
let chartContainer = html`<div class=chartcontainer></div>`;
let nonEmptyTimesSet = new Set(coraisData.filter(c=>c.recife == recife).map(c=>c.time));
let timeMap = new Map();
let indicadorDomain = [];
for (let a of aguaData) {
if (a.recife != recife) continue;
let obj = (timeMap.has (a.time)) ? timeMap.get(a.time) : {time:a.time};
obj[a.indicador] = a.value;
indicadorDomain.push(a.value);
timeMap.set(a.time,obj)
}
let nonEmptyTimes = Array.from(nonEmptyTimesSet);
nonEmptyTimes.sort()
let defTime = mutable timeCurrent;
if (!nonEmptyTimesSet.has(defTime)) {
defTime = nonEmptyTimes[0];
mutable timeCurrent = defTime;
}
let keys = new Set();
if (speciesInfo[species]) {
let animalType = speciesInfo[species].tipo;
let [cdata,yspec] = (
{"peixe" : [peixesData, { "field": "size", "legend": {"title": species } }],
"coral": [coraisData, { "field": "tipo", "legend": {"title": species } }],
"invertebrado": [invertebradosData, { "field": "species", "legend": {"title": "Invertebrado"} }]
})[animalType];
for (let c of cdata) {
if (c.recife == recife && c.species == species) {
let obj = (timeMap.has (c.time)) ? timeMap.get(c.time) : {time:c.time};
let key = c [yspec.field];
keys.add(key)
obj[key] = c.value;
timeMap.set(c.time,obj)
}
}
}
let data = Array.from(timeMap).map(([key,value]) => value);
data.sort((a,b)=> a.time-b.time);
//
// The x scale
//
let x = d3.scaleBand()
.domain(nonEmptyTimes)
.range([margin.left, w - margin.right])
.padding(0.4);
let grid = g => g
.attr("stroke", "#C0C0C0")
.call(g => g.append("g")
.selectAll("line")
.data(x.domain())
.join("line")
.attr("x1", d => x(d) + x.bandwidth()/2)
.attr("x2", d => x(d) + x.bandwidth()/2)
.attr("y1", margin.top)
.attr("y2", h - margin.bottom));
let formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("pt");
const svg = d3.create("svg")
.attr("width", w)
.attr("height", h)
let filter = svg.append("defs").append("filter")
.attr("id", "dropshadow")
.append("feDropShadow")
.attr("dx",0.2)
.attr("dy",1)
.attr("stdDeviation",1)
.attr("flood-color",'gray')
let background = svg.append("g");
background.append("rect")
.attr("fill", "#D3D3D3")
.attr("opacity", 1)
.attr("x", margin.left)
.attr("y", margin.top)
.attr("width", w-margin.left-margin.right)
.attr("height", h-margin.top-margin.bottom);
background.call(grid);
const svgTime = d3.create("svg")
.attr("z-index", 120)
.attr("width", w)
.attr("height", hTime);
//
// The x Axis and labels
//
let firstYear = new Set();
let yearTicks = new Set();
nonEmptyTimes.forEach(t => {
let year = Math.trunc(t);
if (firstYear.has(year)) return;
firstYear.add(year);
yearTicks.add(t)
})
let xrange = x.range();
let shortLabels = (xrange[1]-xrange[0])/nonEmptyTimes.length < 24;
let xAxis = g => g
.attr("transform", `translate(0,${hTime - margin.bottomTime})`)
.call(d3.axisTop(x)
.tickFormat((d,i) => {
let {ano,mes} = timeToAnoMes(d)
return shortLabels && !yearTicks.has(d) ? mes[0] : mes;
}))
let xAxisYear = g => g
.attr("transform", `translate(0,${hTime - margin.bottomTime})`)
.call (d3.axisTop(x)
.tickFormat((d,i) => {
if (!yearTicks.has(d)) return "";
let {ano,mes} = timeToAnoMes(d)
return `${ano}`
}))
let curTimeRule = svgTime.append("g").append("line")
.attr("transform", `translate(0,${hTime - margin.bottomTime})`)
.style ("stroke", "currentColor")
.style ("opacity", 0.3)
.style ("stroke-width", "8")
.attr("x1", x.range()[0])
.attr("y1", -3)
.attr("y2", -3)
let monthAxis = svgTime.append("g").attr("class", "timeaxis").call(xAxis);
let yearAxis = svgTime.append("g").attr("class", "timeaxis").call(xAxisYear);
let pointer = svgTime.append("g")
.append("path")
.attr ("class", "timeCursor")
.attr("d", "M-2,0 l0,-6 l4,0 l0,30 l-4,0 z")
// Buttons to change time
// let nextButton = svgTime.append("g")
// .attr("class", "nextButton")
// .append("polygon")
// .attr("transform", `translate(${x.range()[0]-20},${hTime - margin.bottomTime-3}) scale(2)`)
// .attr("points", "-4,-4 -4,4 4,0")
// .attr("fill", "black")
// .on ("click", () => {
// let t = mutable timeCurrent;
// let i = nonEmptyTimes.indexOf(t);
// if (i >= 0 && i+1 < nonEmptyTimes.length) {
// mutable timeCurrent = nonEmptyTimes[i+1]
// }
// })
// Function to highlight the current time text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
let highlightTime = function (time) {
monthAxis.selectAll(".tick text")
.style("font-weight", d => d == time ? "bold" : "normal")
.style("color", d => d == time ? "#FF7A00" : "#000000")
.style("text-shadow", d => d == time ? "0px 2px 4px rgba(0, 0, 0, 0.18)" : "none")
.style("font-size", d => d == time ? "120%" : "100%");
yearAxis.selectAll(".tick text")
.style("font-weight", d => d == time ? "bold" : "normal")
.style("font-size", d => d == time ? "120%" : "100%")
.style("color", d => d == time ? "#FF7A00" : "#000000")
.style("text-shadow", d => d == time ? "0px 2px 4px rgba(0, 0, 0, 0.18)" : "none")
.attr("dy","-1.2em");
curTimeRule.attr("x2", x(time)+x.bandwidth()*0.5);
pointer.attr("transform", `translate(${x(time)+x.bandwidth()*0.5},${hTime - margin.bottomTime})`);
}
// The time cursor
pointer.call(
d3.drag()
.on("drag", function () {
d3.select(this)
.attr("transform", `translate(${d3.event.x},${hTime - margin.bottomTime})`)
})
.on("end", function () {
let time = 0;
let closest = 1e10;
for (let t of nonEmptyTimes) {
let dist = Math.abs(x(t)+x.bandwidth()*0.5 - d3.event.x);
if (dist < closest) {
time = t;
closest = dist;
}
}
highlightTime(time)
mutable timeCurrent = time;
})
);
highlightTime (defTime);
svgTime.selectAll(".timeaxis .tick")
.style("cursor", "pointer")
.on ("click", (d) => {
highlightTime(d);
mutable timeCurrent = d;
});
//
// The legend for the species data
//
let legend = html`<div class="legendcontainer"></div>`;
// A tooltip element general
let tooltip = d3.select(chartContainer).append("div")
.attr("class", "tooltip");
//
// Draws the whitening (branqueamento) dots
//
let plotBranqueamento= function (species) {
let bdata = nonEmptyTimes.map(t => ({time: t, value: getBranqueamento(t)/7}));
let branqueamento = svg.append("g")
.attr("class", "branqueamento")
branqueamento.append("line")
.attr("x1", xrange[0])
.attr("x2", xrange[1])
.attr("y1",h-margin.bottom/2)
.attr("y2",h-margin.bottom/2)
let branqueamentoColor = a => {
let [r,g,b] = [255,127+a*128,a*255];
return `rgb(${r},${g},${b})`
}
let branqueamentoTitle = d => {
let i = +Math.ceil(d*7);
return ["0", "<5%", "6-10%", "11-20%","21-40%","41-60%","61-80%","81-100%"][i]
}
let branqueamentoTooltip =
circle => {
circle
.on("mouseover", function(d) {
let [x,y]=[d3.event.pageX,d3.event.pageY]
tooltip
.style("top", `${y-20}px`)
.style("left",`${x-20}px`)
tooltip.style("visibility", "visible")
.html(`<span>${branqueamentoTitle(d.value)}</span>`);
})
.on("mousemove", function() {
let [x,y]=[d3.event.pageX,d3.event.pageY]
tooltip
.style("top", `${y-20}px`)
.style("left",`${x-20}px`)
})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");
});
}
branqueamento
.selectAll("circle")
.data(bdata)
.join("circle")
.attr("stroke", "rgb(255,127,0)")
.attr("stroke-width", 1)
.attr("fill", d => branqueamentoColor(d.value))
.attr("cx", d => x(d.time)+x.bandwidth()*0.5)
.attr("cy", h-margin.bottom/2)
.attr("r", 4)
.call(branqueamentoTooltip)
//.append("title")
//.text(d =>branqueamentoTitle(d.value));
let legData = [0,0.25,0.5,0.75,1];
let hl=55;
let xb = d3.scaleLinear()
.domain([0,1])
.range([4, 80]);
let branqueamentoLegend = d3.create("svg")
.attr("class", "branqueamento")
.attr("width", 100)
.attr("height", hl);
branqueamentoLegend.append("text")
.text("Branqueamento(%)")
.attr("y", hl-40);
branqueamentoLegend.append("line")
.attr("x1", xb(0))
.attr("x2", xb(1))
.attr("y1",hl-margin.bottom/2)
.attr("y2",hl-margin.bottom/2)
let branqueamentoItems = branqueamentoLegend
.selectAll("g")
.data(legData)
.join("g");
branqueamentoItems
.append("circle")
.attr("stroke", "rgb(255,127,0)")
.attr("stroke-width", 1)
.attr("fill", branqueamentoColor)
.attr("cx", d => xb(d))
.attr("cy", hl-margin.bottom/2)
.attr("r", 4);
branqueamentoItems
.append("text")
.style("text-anchor", "middle")
.attr("fill", 'black')
.attr("x", d => xb(d))
.attr("y", hl-margin.bottom/2-10)
.text(d=>`${d*100}`)
legend.append(branqueamentoLegend.node())
}
let plotSpecies = function (species) {
//
// The bar chart for the species
//
keys = Array.from(keys);
// Aggregate the bars into series
let series = d3.stack()
.keys(keys)
.value((d,key) => d[key] || 0)
(data)
.map((d,i) => (d.forEach(v => v.key = keys[i]), d));
// The species y scale
let y = d3.scaleLinear()
.domain([0, d3.max(series, d => d3.max(d, d => d[1]))])
.rangeRound([h - margin.bottom, margin.top]);
// The color palette
let colorPalette = [ "rgb(255,122,0)", "rgb(197,95,0)", "#7B3D04", "green", "yellow" ];
// The series color mapping
let color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(colorPalette)
.unknown("#ccc")
// The tooltip
let stackTooltip =
rect => {
rect
.on("mouseover", function(d) {
let [x,y]=[d3.event.pageX,d3.event.pageY]
tooltip
.style("top", `${y-20}px`)
.style("left",`${x-20}px`)
tooltip.style("visibility", "visible")
.html(`<span>${d.key}: ${formatValue(d.data[d.key])}</span>`);
})
.on("mousemove", function() {
let [x,y]=[d3.event.pageX,d3.event.pageY]
tooltip
.style("top", `${y-20}px`)
.style("left",`${x-20}px`)
})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");
});
}
// The stacked bars
svg.append("g")
.selectAll("g")
.data(series)
.join("g")
.attr("fill", d => color(d.key))
.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", (d, i) => x(d.data.time))
.attr("y", d => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth())
.call(stackTooltip);
// .append("title")
// .text(d => `${d.key} ${formatValue(d.data[d.key])}`);
// The species y axis
let yAxis = g => g
.attr("class", "species")
.attr("transform", `translate(${w-margin.right},0)`)
.call(d3.axisRight(y).ticks(null, "s"));
svg.append("g")
.call(yAxis);
// Populate the legend panel with the appropriate title and swatches
//legend.append(html`<p class="legend">${speciesInfo[species].nome}</p>`);
legend.append(html`<table class="legend"><tr><td>
${speciesInfo[species].tipo == "peixe"?"Tamanho dos peixes":speciesInfo[species].tipo == "coral"?"Tipos de corais":speciesInfo[species].tipo == "invertebrado"?"Quantidade encontrada":''}
</td><td>
</td></tr>
</table>`);
console.log(color)
legend.append(swatches({
color: color,
columns: "100px"
}));
}
if (speciesInfo[species]) {
plotSpecies (species);
if (speciesInfo[species].tipo == "coral") plotBranqueamento(species)
}
let plotAgua = function (indicador) {
//
// Create a scale for the currently selected indicator
//
let yAgua = d3.scaleLinear().rangeRound([h - margin.bottom, margin.top]);
let yAguaOptions = plotOptionsAgua[indicador];
if (yAguaOptions) {
yAgua.domain(yAguaOptions.scale.domain);
}
else {
yAgua.domain([0, d3.max(indicadorDomain)])
}
// Remove previous plot if any
svg.selectAll(".agua").remove();
// The tooltip
let aguaTooltip =
rect => {
rect
.on("mouseover", function(d) {
let [x,y]=[d3.event.offsetX,d3.event.offsetY]
tooltip
.style("top", `${y+50}px`)
.style("left",`${x+120}px`)
.style("border-color","#38a0e8")
tooltip.style("visibility", "visible")
.html(`<p>ok ${d}</d>`)
console.log(d);
})
.on("mousemove", function() {
let [x,y]=[d3.event.offsetX,d3.event.offsetY]
tooltip
.style("top", `${y+50}px`)
.style("left",`${x+120}px`)
})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");
});
}
//
// The line chart for the selected water indicator
//
let aguaLine = d3.line()
.defined(d => !isNaN(d[indicador]))
.x(d => x(d.time))
.y(d => yAgua(d[indicador]))
let pt = svg.append("path")
.attr("class", "agua")
.datum(data.filter (d=>nonEmptyTimesSet.has(d.time)))
// .attr("stroke", "#38C0F8")
// .attr("stroke-width", 3)
pt.attr("fill", "none")
.attr("transform", `translate(${0.5*x.bandwidth()},0)`)
.style("filter", "url(#dropshadow)")
.attr("d", aguaLine)
//.datum(d =>x(d[0].time))
//.join("rect")
//.call(aguaTooltip)
;
// The y axis for the water indicator
let yAguaAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yAgua).ticks(null, "s"))
svg.append("g")
.attr("class", "agua")
.call (yAguaAxis);
}
plotAgua(mutable indicadorCurrent);
// The water indicator selector
let indicadorMenu = html`<div class="indicadorMenu">
<p>Dados Ambientais</p>
</div>`;
d3.select(indicadorMenu).selectAll("div")
.data(['Temperatura (C)', 'Luminosidade (Lux)', 'pH'])
.join("div")
.attr ("class", "indicadorMenuItem")
.classed ("selected", d => d == mutable indicadorCurrent)
.style("white-space","nowrap")
.style("overflow", "hidden")
.style("text-overflow", "ellipsis")
.on ("click", d => {
mutable indicadorCurrent = d;
plotAgua(d);
d3.select(indicadorMenu).selectAll("div.indicadorMenuItem")
.classed ("selected", d => d == mutable indicadorCurrent)
})
.text(d => d);
// Return all components packed into one div
legend.style.width = "100px";
legend.style.display = "inline-block";
legend.position = "relative";
legend.style ["vertical-align"] = "top";
let chart = svg.node();
chart.style.display = "inline-block";
chartContainer.append(html`<div class=chartAxis>${svgTime.node()}</div>`);
chartContainer.append(html`<div class=chart>${indicadorMenu}${chart}${legend}</div>`);
return chartContainer
}