charts/scatterplot.mjs (215 lines of code) (raw):

import dataTools from "./shared/dataTools" import Tooltip from "./shared/tooltip" import ColorScale from "./shared/colorscale" import { numberFormat, mustache, mobileCheck, bufferize, validateString, getLabelFromColumn } from './shared/toolbelt'; import { addLabels } from "./shared/labels" export default class Scatterplot { constructor(settings) { this.settings = settings this.init() } init() { if (this.settings.tooltip != "") { this.tooltip = new Tooltip(this.settings.tooltip) } this.render() } render() { let { modules, height, width, isMobile, colors, datum, keys, data, title, subtitle, source, tooltip, marginleft, margintop, marginbottom, marginright, trendline, enableShowMore, aria, colorScheme, labels, userkey, type, dropdown, xColumn, yColumn, xAxisLabel, yAxisLabel, groupBy, yMin, xMin, xMax, yMax, hideKey, zColumn, zMin, zMax, zScale, zLabel, opacity, xFormat, columns } = this.settings console.log(`Opacity: ${opacity / 100}`) const $tooltip = (this.tooltip) ? this.tooltip : false d3.select("#graphicContainer svg").remove(); const chartKey = d3.select("#chartKey") chartKey.html("") isMobile = mobileCheck() width = document.querySelector("#graphicContainer").getBoundingClientRect().width height = isMobile ? width * 0.7 : width * 0.5; width = width - marginleft - marginright height = height - margintop - marginbottom const xRange = d3.extent(data.map(d => d[xColumn])) const yRange = d3.extent(data.map(d => d[yColumn])) const zRange = (zColumn in data[0]) ? d3.extent(data.map(d => d[zColumn])) : null const groupData = data.map(d => d[groupBy]) const keyData = Array.from(new Set(groupData)); const svg = d3.select("#graphicContainer").append("svg") .attr("width", width + marginleft + marginright) .attr("height", height + margintop + marginbottom) .append("g") .attr("transform", `translate(${marginleft}, ${margintop})`); colors = new ColorScale() const keyColor = dataTools.getKeysColors({ keys: keys, userKey: userkey, option: { colorScheme : colorScheme } }) colors.set(keyColor.keys, keyColor.colors) console.log(columns) if (!hideKey) ( keyData.forEach((key, i) => { const keyDiv = chartKey .append("div") .attr("class", "keyDiv") keyDiv .append("span") .attr("class", "keyCircle") .style("background-color", () => colors.get(key)) .style("opacity", opacity / 100) keyDiv .append("span") .attr("class", "keyText") .text(key) //getLabelFromColumn(columns, key) }) ) const labelsXY = [] data.forEach((d) => { if ("label" in d) { if (d.label === "TRUE") { labelsXY.push(d) } } }) yMin = (!isNaN(yMin) && yMin != "") ? yMin : yRange[0] yMax = (!isNaN(yMax) && yMax != "") ? yMax : yRange[1] xMin = (!isNaN(xMin) && xMin != "") ? xMin : xRange[0] xMax = (!isNaN(xMax) && xMax != "") ? xMax : xRange[1] const x = d3.scaleLinear() .domain(bufferize(xMin,xMax)) .range([ 0, width ]); svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); const y = d3.scaleLinear() .domain(bufferize(yMin,yMax)) .range([ height, 0]); const z = (zRange != null) ? d3[zScale]() .domain(zRange) .range([zMin, zMax]) : null svg.append("g") .call(d3.axisLeft(y)); svg.append('g') .selectAll("dot") .data(data) .enter() .append("circle") .attr("cx", (d) => x(d[xColumn])) .attr("cy", (d) => y(d[yColumn])) .attr("r", (d) => { return (z) ? z(d[zColumn]) : 3 }) .style("fill", (d) => colors.get(d[groupBy])) .style("opacity", opacity / 100) if ($tooltip) { $tooltip.bindEvents( d3.selectAll("circle"), width, height ) } if (xAxisLabel) { svg .append("text") .attr("x", width) .attr("y", height - 6) .attr("fill", "#767676") .attr("text-anchor", "end") .text(xAxisLabel) } if (yAxisLabel) { svg .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("fill", "#767676") .attr("text-anchor", "end") .text(yAxisLabel) } // Highlight dots if their label property is set to TRUE if (labelsXY.length > 0) { svg .selectAll(".dot-label") .data(labelsXY) .enter() .append("text") .attr("class", "dot-label") .attr("x", (d) => x(d[xColumn])) .attr("dy", (d) => { return (z) ? z(d[zColumn]) + 10 : 15 }) .attr("text-anchor", "middle") .attr("y", (d) => y(d[yColumn])) .text(function (d) { return zLabel != "" ? d[zLabel] : "" }) } if (trendline.length > 0) { let default_filter = "default" let trend = trendline.filter((value) => value.trendline == default_filter) if (trend.length == 0) { trend = trendline.filter((value) => value.trendline == "default") } const x1 = parseFloat(trendline[0].min_x) const y1 = parseFloat(trendline[0].min_y) const x2 = parseFloat(trendline[0].max_x) const y2 = parseFloat(trendline[0].max_y) const trendData = [[x1, y1, x2, y2]] const tline = svg. selectAll(".trendline") .data(trendData) tline .enter() .append("line") .attr("class", "trendline") .attr("x1", (d) => x(d[0])) .attr("y1", (d) => y(d[1])) .attr("x2", (d) => x(d[2])) .attr("y2", (d) => y(d[3])) .attr("stroke", "black") .attr("stroke-width", 1) .style("opacity", 1) .style("stroke-dasharray", "3, 3") } } }