UNPKG

terriajs

Version:

Geospatial data visualization platform.

100 lines 4.24 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { GlyphCircle } from "@visx/glyph"; import { scaleLinear } from "@visx/scale"; import { interpolateNumber as d3InterpolateNumber } from "d3-interpolate"; import { observer } from "mobx-react"; import { forwardRef, useImperativeHandle, useMemo } from "react"; import Glyphs from "./Glyphs"; const _MomentPointsChart = function MomentPointsChart(props, ref) { const { id, chartItem, basisItem, basisItemScales, scales, glyph = "circle" } = props; const points = useMemo(() => { if (basisItem && basisItemScales) { // We want to stick the chartItem points to the basis item, to do this we // interpolate the chart item points to match the basis item points. This // interpolation should not affect the scale of the chart item points. const basisToSourceScale = scaleLinear({ domain: basisItemScales.y.domain(), range: scales.y.domain() }); const interpolatedPoints = chartItem.points.map((p) => ({ ...p, ...interpolate(p, basisItem.points, basisToSourceScale) })); return interpolatedPoints; } return chartItem.points; }, [chartItem, basisItem, basisItemScales, scales]); // Allow BottomDockChart and others to control zoom useImperativeHandle(ref, () => ({ doZoom(scales) { if (points.length === 0) { return; } const vGlyphs = document.querySelectorAll(`g#${id} > g.visx-glyph`); vGlyphs.forEach((vGlyph, i) => { const point = points[i]; if (point) { const left = scales.x(point.x); const top = scales.y(point.y); const scale = point.isSelected ? "scale(1.4, 1.4)" : ""; vGlyph.setAttribute("transform", `translate(${left}, ${top}) ${scale}`); vGlyph.setAttribute("fill-opacity", `${point.isSelected ? 1.0 : 0.3}`); } }); } }), [id, points]); const baseKey = `moment-point-${chartItem.categoryName}-${chartItem.name}`; const fillColor = chartItem.getColor(); const isClickable = chartItem.onClick !== undefined; const clickProps = (point) => { if (isClickable) { return { pointerEvents: "all", cursor: "pointer", onClick: () => chartItem.onClick(point) }; } return {}; }; const Glyph = Glyphs[glyph] ?? GlyphCircle; return (_jsx("g", { id: id, children: points.map((p, i) => (_jsx(Glyph, { left: scales.x(p.x), top: scales.y(p.y), size: 100, fill: fillColor, fillOpacity: p.isSelected ? 1.0 : 0.3, ...clickProps(p) }, `${baseKey}-${i}`))) })); }; /** Interpolates the given source point {x, y} to the closet point in the `sortedPoints` array. * * The source point and `sortedBasisPoints` may be of different scale, so we use `basisToSourceScale` * to generate a point in the original source items scale. */ function interpolate(p, sortedBasisPoints, basisToSourceScale) { // MomentPointsChart always has Dates for x coordinates const x = p.x; const closest = closestPointIndex(x, sortedBasisPoints); if (closest === undefined) { return p; } const a = sortedBasisPoints[closest]; const b = sortedBasisPoints[closest + 1]; if (a === undefined || b === undefined) { return p; } const aTime = a.x.getTime(); const bTime = b.x.getTime(); const xAsPercentage = (x.getTime() - aTime) / (bTime - aTime); const interpolated = { x, y: d3InterpolateNumber(basisToSourceScale(a.y), basisToSourceScale(b.y))(xAsPercentage) }; return interpolated; } function closestPointIndex(x, sortedPoints) { const xTime = x.getTime(); const index = sortedPoints.findIndex((p) => p.x.getTime() >= xTime); if (index === -1) { return undefined; } if (index === 0) { return 0; } return index - 1; } export default observer(forwardRef(_MomentPointsChart)); //# sourceMappingURL=MomentPointsChart.js.map