Published unlisted
Edited
Feb 11, 2021
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
width = 1024
Insert cell
Insert cell
converteLegendaCallback = setTimeout(converteLegenda,500);
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable sideCount = 0
Insert cell
mutable bottomCount = 0
Insert cell
Insert cell
Insert cell
chart = d3PlotEspecieIndicador(recifeCurrent,selectedElement,indicadorCurrent)
Insert cell
Insert cell
Insert cell
card = cardInfo(selectedElement)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
coloniasHelp = html` `//main.querySelector(".sidepanel .species .categoria .coral");
Insert cell
popups = {
title;
setMainDisplay;
speciesTable;
removePopups(title);
removePopups(main);
let titulo = title.querySelector("#titulo");
let colonias = coloniasHelp
let pop1 = await addPopupHelp(titulo, {
contents: html`<div style="width:350px;padding-right:12px;">${textoTitulo}</div>`,
mode:'i',
popx:30, popy:0,
insetCloseIcon:true,
insetTop:0, insetRight:0
});
await Promises.delay(1000); // Wait for the diorama popup help image to load
let dioramaWidth = renderer.domElement.getBoundingClientRect().width;
let pop2 = await addPopupHelp(renderer.domElement,
{insetCloseIcon:true,
anchor:'ne', popy:3,
popx:0,//35-dioramaPopup.width-(dioramaWidth-dioramaPopup.width)/2,
insetTop:0, insetRight:0, contents:dioramaPopup,
offsetx:60-dioramaWidth, offsety: 12,
});
pop2.popbox.style.padding = '0px';
pop2.popbox.style.width = (dioramaPopup.width-2) + 'px';
pop2.popbox.style.height = (dioramaPopup.height-2) + 'px';
let recife = title.querySelector("#menurecife")
let recifeHelp = html`<div style="width:350px;padding-right:8px;">${textoRecife[mutable recifeCurrent]}</div>`;
let pop3 = await addPopupHelp(recife, {
mode:'i',
contents: recifeHelp, offsetx: (recifeCurrent==="RFO"?-75:-110), offsety: 2,
popx:-380, popy:0,
insetCloseIcon:true,
insetTop:0, insetRight:0});
let pop4 = await addPopupHelp(colonias, {
mode:'i',
contents: html`<div style="opacity:1;color:black;width:100px;font-size:12px">Recrutas são colônias pequenas</div>`, offsetx:133, offsety: 20,
popx:-60, popy:30,
insetCloseIcon:true,
insetTop:0, insetRight:0});
return [pop1,pop2,pop3,pop4]
}
Insert cell
Insert cell
Insert cell
Insert cell
dioramaPopup = {
let svg = await FileAttachment("popup diorama5.svg").image();
return svg
}
Insert cell
`styles`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
speciesPanelWidth = 215
Insert cell
mutable drawingWidth = width
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
coralVivoDb = JSON.parse(await FileAttachment("coralDb@4.json").text())
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3PlotEspecieIndicador = function (recife, species, indicador) {

// Geometric parameters
let [w,h,hTime] = [width-250,164,50];
let margin = {left:40, right:40, top:0, bottom: 30, bottomTime: 20};
// The chart container
let chartContainer = html`<div class=chartcontainer></div>`;
// Find what times are defined for the recife in coraisData
let nonEmptyTimesSet = new Set(coraisData.filter(c=>c.recife == recife).map(c=>c.time));
// Map times to the appropriate water indicator data
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};
//nonEmptyTimes.add(a.time);
obj[a.indicador] = a.value;
indicadorDomain.push(a.value);
timeMap.set(a.time,obj)
}
// Transform the times to an ordered array
let nonEmptyTimes = Array.from(nonEmptyTimesSet);
nonEmptyTimes.sort()

// Select an appropriate default time
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
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
let species = [... new Set(peixesData.map(c => c.species))];
return species.map(s => speciesInfo[s] ? speciesInfo[s] : s)
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require.alias({'d3-selection': 'd3'})('d3@5', 'd3-simple-slider')
Insert cell
import {swatches} from "@d3/color-legend"
Insert cell
import {circleLayout, rectLayout} from "@esperanc/circle-packing-with-force-boundary"
Insert cell
stringsim = require('https://bundle.run/string-similarity@4.0.1')
Insert cell
import { slider, select } from "@bartok32/diy-inputs"
Insert cell
import { addPopupHelp, removePopups } from "230e02c4ae34f192"
Insert cell
import {importjs,importcss} from "@chomtana/import-external-library"
Insert cell
FontAwesome = importcss("https://use.fontawesome.com/releases/v5.7.2/css/all.css")
Insert cell
Insert cell
Insert cell
mutable timeCurrent = 2018.02
Insert cell
mutable recifeCurrent = 'ARA'
Insert cell
mutable indicadorCurrent = Array.from (indicadores)[1]
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more