/**
* Info Card showing details when hovering over a stipple
*/
class Card {
/**
*
* @param data holding the detailed meta information about individual data points
* @param width of the card div
* @param height of the card div
* @param div where the info card should appear
*/
constructor(data, width, height, div) {
this.data = data;
this.width = width;
this.height = height;
this.svg = this.setupSVG();
this.circleRadius = 2.0;
this.lineWidth = 1.0;
}
setupSVG() {
return d3.select('#cardDiv')
.append('svg')
.attr('width', this.width)
.attr('height', this.height);
}
/**
*
* @param bounds of the region that is investigated in more detail
* @param voronoi
* @param voronoiIndex
*/
drawArea(bounds, voronoi, voronoiIndex) {
let [maxX, maxY] = bounds.max;
let [minX, minY] = bounds.min;
maxX = Math.ceil(maxX);
maxY = Math.ceil(maxY);
minY = Math.floor(minY);
minX = Math.floor(minX);
let croppedData = [];
for (let x = minX; x < maxX; x++) {
for (let y = minY; y < maxY; y++) {
let thisVoronoiIndex = getVoronoiCell([x, y], voronoi);
if (thisVoronoiIndex === voronoiIndex) { // check whether the coordinate is in the voronoi region
const idx = (y * this.data.width) + x;
croppedData = croppedData.concat(this.data.data[idx]);
}
}
}
this.svg.selectAll("*").remove(); // clear svg before plotting new elements
const projection = createProjection(this.data.width, this.data.height)
let dx = maxX - minX;
let dy = maxY - minY;
let x = (minX + maxX) / 2;
let y = (minY + maxY) / 2;
let scale = 1.0 / Math.max(dx / this.width, dy / this.height);
let translate = [this.width / 2 - scale * x, this.height / 2 - scale * y];
let g = this.svg.append("g")
.attr('transform', 'translate(' + translate + ')scale(' + scale + ')')
.attr('width', this.width)
.attr('height', this.height);
let lineFunc = d3.line()
.x(function (d) {
return d[0];
})
.y(function (d) {
return d[1];
});
g.append('path')
.attr('d', lineFunc(voronoi.cellPolygon(voronoiIndex)))
.attr('stroke', 'black')
.attr('stroke-width', this.lineWidth * (1 / scale))
.attr('fill', 'none');
g.selectAll('circle')
.data(croppedData).enter()
.append('circle')
.attr('cx', function (d) {
const cx = projection([d.LON, d.LAT])[0];
const cy = projection([d.LON, d.LAT])[1];
if (getVoronoiCell([cx, cy], voronoi) === voronoiIndex) {
return cx;
} else {
return 0;
}
})
.attr('cy', function (d) {
const cx = projection([d.LON, d.LAT])[0];
const cy = projection([d.LON, d.LAT])[1];
if (getVoronoiCell([cx, cy], voronoi) === voronoiIndex) {
return cy;
} else {
return 0;
}
})
.attr('r', this.circleRadius * (2 / scale))
.style('fill', '#4CAF50');
}
}