viewof dorling = {
interfaceToggle;
remakeMapa;
mutable reactDebug.push("dorling");
let [[xmin, ymin], [xmax, ymax]] = limites;
let width = panel.mapa.w;
let height = Math.max(panel.mapa.h);
let mapScale = Math.max(0.2, Math.min (3,
(width - 318 * (mutable filtrarPorRolldownToggle ? 2 : 1)) / (xmax - xmin)));
const container = html`<div style="position:relative;width:${width}px;height:${height}px;">`;
const zoomIn = zoomInIcon;
Object.assign(zoomIn.style, {
position: "absolute",
zIndex: 10,
top: "10px",
left: interfaceToggle ? "328px" : "10px"
});
const zoomOut = zoomOutIcon;
Object.assign(zoomOut.style, {
position: "absolute",
zIndex: 10,
top: "38px",
left: interfaceToggle ? "328px" : "10px"
});
container.append(zoomIn, zoomOut);
zoomIn.onclick = () => doZoom(12 / 10);
zoomOut.onclick = () => doZoom(10 / 12);
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("id", "dorling");
container.append(svg.node());
container.value = [];
container.hovered;
//
// Um grupo para conter o mapa propriamente dito.
// Precisamos dele para poder aplicar translações mais facilmente
//
const mainGroup = svg.append("g").attr("class", "mainGroup");
//
// Tratamento de eventos para translação e escala
//
let translation = [interfaceToggle ? 328 : 0, 0],
mousedown = null;
svg
.on("mousedown", function (e) {
mousedown = [e.offsetX, e.offsetY];
svg.style("cursor", "grab");
})
.on("mousemove", function (e) {
if (!mousedown) return;
let mouse = [e.offsetX, e.offsetY];
let displacement = [mouse[0] - mousedown[0], mouse[1] - mousedown[1]];
translation[0] += displacement[0];
translation[1] += displacement[1];
mainGroup.attr("transform", `translate(${translation})`);
mousedown = mouse;
})
.on("mouseup", function (e) {
if (!mousedown) return;
svg.style("cursor", "auto");
mousedown = null;
})
.on("wheel", function (e) {
e.preventDefault();
e.stopPropagation();
const scaleFactor = e.deltaY > 0 ? 11 / 10 : 10 / 11;
const center = [e.offsetX, e.offsetY];
doZoom(scaleFactor, center);
// mutable debug = {x : e.offsetX, y:e.offsetY, delta:[e.deltaX, e.deltaY, e.deltaZ, e.deltaMode]};
});
mainGroup.attr("transform", `translate(${translation})`);
//
// Grupo contendo os círculos. Só este é escalado
//
let circlesGroup = mainGroup
.append("g")
.classed("circles", true)
.attr("transform", `scale(${mapScale},${mapScale})`);
//
// Círculos para os municípios
// Apenas as posições são configuradas aqui. As demais (raio, cor, etc)
// são configuradas externamente pela célula dorlingCircleConf, evitando
// assim o overhead de recriar os círculos quando os filtros mudam
//
let circles = circlesGroup
.selectAll("circle")
.data(posicoesFinais)
.join("circle") // joins the data to the empty circles selected
.on("dblclick", dbclicked)
.classed("circle", true)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);
//
// Tooltips para os círculos
var tip = d3tip().html(
(event, d) => `
${tooltip({
pol: dadosPorCodigo.get(d.properties.codarea.slice(0, -1))["AVG_PM25"],
bp:
(1000 *
dadosPorCodigo.get(d.properties.codarea.slice(0, -1))[
"BAIXO_PESO"
]) /
dadosPorCodigo.get(d.properties.codarea.slice(0, -1))["TOTAL"],
municipio: municipioPorCodigo.get(d.properties.codarea)[
"Nome_Município"
],
uf: municipioPorCodigo.get(d.properties.codarea)["Nome_UF"],
populacao: populacaoPorCodigo.get(d.properties.codarea)
})}`
);
svg.call(tip);
circles.on("mouseover", tip.show).on("mouseout", tip.hide);
//
// As bandeirinhas para os municípios selecionados
//
let flags = mainGroup.append("g").classed("flags", true);
const updateFlags = () => {
flags.selectAll("path").remove();
flags
.selectAll("path")
.data(viewof munSelecionados.value)
.join("path")
.attr("transform", (d) => {
let pos = posicoesPorCod.get(d.cod);
return `translate(${pos.x * mapScale},${pos.y * mapScale})`;
})
.attr("d", flagPath(12))
.attr("fill", (d) => d.cor)
.attr("stroke", "black");
};
//
// Realiza o zoom dos círculos mantendo o ponto center fixo
// Se center não for passado, assume-se o centro do svg
//
function doZoom(scaleFactor, center) {
const [cx, cy] = center || [width / 2, height / 2];
const [tx, ty] = translation;
const newScale = Math.max(0.2, Math.min (3, mapScale * scaleFactor));
const [x0, y0] = [(cx - tx) / mapScale, (cy - ty) / mapScale];
const [newTx, newTy] = [cx - x0 * newScale, cy - y0 * newScale];
translation = [newTx, newTy];
mapScale = newScale;
mainGroup.attr("transform", `translate(${translation})`);
circlesGroup.attr("transform", `scale(${mapScale},${mapScale})`);
updateFlags();
}
function dbclicked() {
const oldValue = viewof munSelecionados.value;
const circle = d3.select(this);
const findMun = (circle) => {
const codigo = circle.data()[0].properties.codarea.slice(0, -1);
const filtrados = municipios.filter((obj) => obj.cod == codigo);
return filtrados[0];
};
let datum = findMun(circle);
alteraMenuLocalidade(datum);
updateFlags();
}
function updateSelected() {
// mutable TESTE = {}
const oldValue = viewof munSelecionados.value;
const codigos = oldValue.map((d) => d.cod);
circles.each(function (d) {
const circle = d3.select(this);
const selected = codigos.includes(d.properties.codarea.slice(0, -1));
circle.classed("clicked", selected);
});
updateFlags();
}
updateSelected();
container.updateSelected = updateSelected;
// Export the circle selection so that we can configure them externally
container.circleSelection = circles;
return container;
}