@perspective-dev/viewer-charts 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +193 -0
- package/dist/cdn/perspective-viewer-charts.js +3 -0
- package/dist/cdn/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/axis/axis-primitives.d.ts +24 -0
- package/dist/esm/axis/bar-axis.d.ts +51 -0
- package/dist/esm/axis/canvas.d.ts +24 -0
- package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
- package/dist/esm/axis/categorical-axis.d.ts +27 -0
- package/dist/esm/axis/facet-chrome.d.ts +13 -0
- package/dist/esm/axis/label-geometry.d.ts +41 -0
- package/dist/esm/axis/legend.d.ts +44 -0
- package/dist/esm/axis/numeric-axis.d.ts +20 -0
- package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
- package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
- package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
- package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
- package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
- package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
- package/dist/esm/charts/canvas-types.d.ts +15 -0
- package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
- package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
- package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
- package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
- package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
- package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
- package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
- package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
- package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
- package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
- package/dist/esm/charts/chart-base.d.ts +402 -0
- package/dist/esm/charts/chart.d.ts +338 -0
- package/dist/esm/charts/common/band-layout.d.ts +32 -0
- package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
- package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
- package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
- package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
- package/dist/esm/charts/common/leaf-color.d.ts +33 -0
- package/dist/esm/charts/common/node-store.d.ts +81 -0
- package/dist/esm/charts/common/tree-chart.d.ts +48 -0
- package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
- package/dist/esm/charts/common/tree-data.d.ts +54 -0
- package/dist/esm/charts/common/visible-extent.d.ts +51 -0
- package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
- package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
- package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
- package/dist/esm/charts/map/map.d.ts +67 -0
- package/dist/esm/charts/registry.d.ts +14 -0
- package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
- package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
- package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
- package/dist/esm/charts/series/series-build.d.ts +228 -0
- package/dist/esm/charts/series/series-interact.d.ts +35 -0
- package/dist/esm/charts/series/series-render.d.ts +41 -0
- package/dist/esm/charts/series/series-type.d.ts +49 -0
- package/dist/esm/charts/series/series.d.ts +317 -0
- package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
- package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
- package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
- package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
- package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
- package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
- package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
- package/dist/esm/charts/treemap/treemap.d.ts +74 -0
- package/dist/esm/config.d.ts +27 -0
- package/dist/esm/data/lazy-row.d.ts +32 -0
- package/dist/esm/data/split-groups.d.ts +20 -0
- package/dist/esm/data/view-reader.d.ts +35 -0
- package/dist/esm/event-detail.d.ts +28 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/interaction/hit-test.d.ts +30 -0
- package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
- package/dist/esm/interaction/host-sink-message.d.ts +46 -0
- package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
- package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
- package/dist/esm/interaction/spatial-grid.d.ts +15 -0
- package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
- package/dist/esm/interaction/zoom-controller.d.ts +106 -0
- package/dist/esm/interaction/zoom-router.d.ts +48 -0
- package/dist/esm/layout/facet-grid.d.ts +126 -0
- package/dist/esm/layout/plot-layout.d.ts +104 -0
- package/dist/esm/layout/ticks.d.ts +17 -0
- package/dist/esm/map/mercator.d.ts +102 -0
- package/dist/esm/map/tile-cache.d.ts +38 -0
- package/dist/esm/map/tile-layer.d.ts +66 -0
- package/dist/esm/map/tile-loader.d.ts +52 -0
- package/dist/esm/map/tile-source.d.ts +66 -0
- package/dist/esm/perspective-viewer-charts.js +3 -0
- package/dist/esm/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/plugin/charts.d.ts +40 -0
- package/dist/esm/plugin/plugin.d.ts +95 -0
- package/dist/esm/render/scheduler.d.ts +41 -0
- package/dist/esm/theme/gradient.d.ts +48 -0
- package/dist/esm/theme/palette.d.ts +13 -0
- package/dist/esm/theme/theme-snapshot.d.ts +7 -0
- package/dist/esm/theme/theme.d.ts +53 -0
- package/dist/esm/transport/protocol.d.ts +430 -0
- package/dist/esm/transport/renderer-transport.d.ts +201 -0
- package/dist/esm/utils/css.d.ts +1 -0
- package/dist/esm/utils/font-snapshot.d.ts +50 -0
- package/dist/esm/webgl/buffer-pool.d.ts +62 -0
- package/dist/esm/webgl/context-manager.d.ts +184 -0
- package/dist/esm/webgl/gradient-texture.d.ts +17 -0
- package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
- package/dist/esm/webgl/plot-frame.d.ts +39 -0
- package/dist/esm/webgl/program-cache.d.ts +13 -0
- package/dist/esm/webgl/shader-manifest.d.ts +53 -0
- package/dist/esm/webgl/shader-registry.d.ts +22 -0
- package/dist/esm/worker/boot.d.ts +0 -0
- package/dist/esm/worker/dispatch.d.ts +9 -0
- package/dist/esm/worker/font-loader.d.ts +2 -0
- package/dist/esm/worker/renderer.worker.d.ts +115 -0
- package/dist/esm/worker/session-host.d.ts +26 -0
- package/package.json +47 -0
- package/src/css/perspective-viewer-charts.css +95 -0
- package/src/ts/axis/axis-primitives.ts +125 -0
- package/src/ts/axis/bar-axis.ts +345 -0
- package/src/ts/axis/canvas.ts +64 -0
- package/src/ts/axis/categorical-axis-core.ts +125 -0
- package/src/ts/axis/categorical-axis.ts +716 -0
- package/src/ts/axis/facet-chrome.ts +42 -0
- package/src/ts/axis/label-geometry.ts +188 -0
- package/src/ts/axis/legend.ts +218 -0
- package/src/ts/axis/numeric-axis.ts +353 -0
- package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
- package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
- package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
- package/src/ts/charts/candlestick/candlestick.ts +367 -0
- package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
- package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
- package/src/ts/charts/canvas-types.ts +30 -0
- package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
- package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
- package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
- package/src/ts/charts/cartesian/cartesian.ts +469 -0
- package/src/ts/charts/cartesian/glyph.ts +81 -0
- package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
- package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
- package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
- package/src/ts/charts/cartesian/label-interner.ts +56 -0
- package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
- package/src/ts/charts/chart-base.ts +840 -0
- package/src/ts/charts/chart.ts +427 -0
- package/src/ts/charts/common/band-layout.ts +63 -0
- package/src/ts/charts/common/categorical-y-chart.ts +81 -0
- package/src/ts/charts/common/category-axis-resolver.ts +314 -0
- package/src/ts/charts/common/chrome-cache.ts +79 -0
- package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
- package/src/ts/charts/common/leaf-color.ts +92 -0
- package/src/ts/charts/common/node-store.ts +235 -0
- package/src/ts/charts/common/tree-chart.ts +76 -0
- package/src/ts/charts/common/tree-chrome.ts +123 -0
- package/src/ts/charts/common/tree-data.ts +623 -0
- package/src/ts/charts/common/visible-extent.ts +112 -0
- package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
- package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
- package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
- package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
- package/src/ts/charts/heatmap/heatmap.ts +368 -0
- package/src/ts/charts/map/map.ts +201 -0
- package/src/ts/charts/registry.ts +65 -0
- package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
- package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
- package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
- package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
- package/src/ts/charts/series/series-build.ts +848 -0
- package/src/ts/charts/series/series-interact.ts +604 -0
- package/src/ts/charts/series/series-render.ts +1109 -0
- package/src/ts/charts/series/series-type.ts +99 -0
- package/src/ts/charts/series/series.ts +794 -0
- package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
- package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
- package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
- package/src/ts/charts/sunburst/sunburst.ts +248 -0
- package/src/ts/charts/treemap/treemap-interact.ts +445 -0
- package/src/ts/charts/treemap/treemap-layout.ts +328 -0
- package/src/ts/charts/treemap/treemap-render.ts +886 -0
- package/src/ts/charts/treemap/treemap.ts +247 -0
- package/src/ts/config.ts +41 -0
- package/src/ts/data/lazy-row.ts +140 -0
- package/src/ts/data/split-groups.ts +97 -0
- package/src/ts/data/view-reader.ts +107 -0
- package/src/ts/event-detail.ts +44 -0
- package/src/ts/index.ts +53 -0
- package/src/ts/interaction/hit-test.ts +106 -0
- package/src/ts/interaction/host-sink-dom.ts +85 -0
- package/src/ts/interaction/host-sink-message.ts +75 -0
- package/src/ts/interaction/lazy-tooltip.ts +102 -0
- package/src/ts/interaction/raw-event-forwarder.ts +175 -0
- package/src/ts/interaction/spatial-grid.ts +100 -0
- package/src/ts/interaction/tooltip-controller.ts +407 -0
- package/src/ts/interaction/zoom-controller.ts +468 -0
- package/src/ts/interaction/zoom-router.ts +230 -0
- package/src/ts/layout/facet-grid.ts +346 -0
- package/src/ts/layout/plot-layout.ts +277 -0
- package/src/ts/layout/ticks.ts +168 -0
- package/src/ts/map/mercator.ts +204 -0
- package/src/ts/map/tile-cache.ts +96 -0
- package/src/ts/map/tile-layer.ts +382 -0
- package/src/ts/map/tile-loader.ts +143 -0
- package/src/ts/map/tile-source.ts +156 -0
- package/src/ts/plugin/charts.ts +286 -0
- package/src/ts/plugin/plugin.ts +668 -0
- package/src/ts/render/scheduler.ts +339 -0
- package/src/ts/shaders/area.frag.glsl +20 -0
- package/src/ts/shaders/area.vert.glsl +19 -0
- package/src/ts/shaders/bar.frag.glsl +25 -0
- package/src/ts/shaders/bar.vert.glsl +60 -0
- package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
- package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
- package/src/ts/shaders/density-extreme.frag.glsl +30 -0
- package/src/ts/shaders/density-mrt.frag.glsl +44 -0
- package/src/ts/shaders/density-mrt.vert.glsl +48 -0
- package/src/ts/shaders/density-resolve.frag.glsl +89 -0
- package/src/ts/shaders/density-resolve.vert.glsl +23 -0
- package/src/ts/shaders/density-splat.frag.glsl +34 -0
- package/src/ts/shaders/density-splat.vert.glsl +52 -0
- package/src/ts/shaders/gridline.frag.glsl +18 -0
- package/src/ts/shaders/gridline.vert.glsl +18 -0
- package/src/ts/shaders/heatmap.frag.glsl +23 -0
- package/src/ts/shaders/heatmap.vert.glsl +42 -0
- package/src/ts/shaders/line-uniform.frag.glsl +26 -0
- package/src/ts/shaders/line-uniform.vert.glsl +54 -0
- package/src/ts/shaders/line.frag.glsl +28 -0
- package/src/ts/shaders/line.vert.glsl +87 -0
- package/src/ts/shaders/scatter.frag.glsl +39 -0
- package/src/ts/shaders/scatter.vert.glsl +67 -0
- package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
- package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
- package/src/ts/shaders/tile.frag.glsl +27 -0
- package/src/ts/shaders/tile.vert.glsl +35 -0
- package/src/ts/shaders/treemap.frag.glsl +19 -0
- package/src/ts/shaders/treemap.vert.glsl +25 -0
- package/src/ts/shaders/y-scatter.frag.glsl +30 -0
- package/src/ts/shaders/y-scatter.vert.glsl +31 -0
- package/src/ts/theme/gradient.ts +312 -0
- package/src/ts/theme/palette.ts +64 -0
- package/src/ts/theme/theme-snapshot.ts +66 -0
- package/src/ts/theme/theme.ts +166 -0
- package/src/ts/transport/protocol.ts +497 -0
- package/src/ts/transport/renderer-transport.ts +788 -0
- package/src/ts/utils/css.ts +36 -0
- package/src/ts/utils/font-snapshot.ts +159 -0
- package/src/ts/webgl/buffer-pool.ts +163 -0
- package/src/ts/webgl/context-manager.ts +414 -0
- package/src/ts/webgl/gradient-texture.ts +84 -0
- package/src/ts/webgl/instanced-attrs.ts +139 -0
- package/src/ts/webgl/plot-frame.ts +91 -0
- package/src/ts/webgl/program-cache.ts +46 -0
- package/src/ts/webgl/shader-manifest.ts +148 -0
- package/src/ts/webgl/shader-registry.ts +97 -0
- package/src/ts/worker/boot.ts +22 -0
- package/src/ts/worker/dispatch.ts +99 -0
- package/src/ts/worker/font-loader.ts +89 -0
- package/src/ts/worker/renderer.worker.ts +734 -0
- package/src/ts/worker/session-host.ts +118 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
function niceNum(value: number, round: boolean): number {
|
|
14
|
+
const exp = Math.floor(Math.log10(value));
|
|
15
|
+
const frac = value / Math.pow(10, exp);
|
|
16
|
+
let nice: number;
|
|
17
|
+
if (round) {
|
|
18
|
+
if (frac < 1.5) {
|
|
19
|
+
nice = 1;
|
|
20
|
+
} else if (frac < 3) {
|
|
21
|
+
nice = 2;
|
|
22
|
+
} else if (frac < 7) {
|
|
23
|
+
nice = 5;
|
|
24
|
+
} else {
|
|
25
|
+
nice = 10;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
if (frac <= 1) {
|
|
29
|
+
nice = 1;
|
|
30
|
+
} else if (frac <= 2) {
|
|
31
|
+
nice = 2;
|
|
32
|
+
} else if (frac <= 5) {
|
|
33
|
+
nice = 5;
|
|
34
|
+
} else {
|
|
35
|
+
nice = 10;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return nice * Math.pow(10, exp);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate an array of "nice" tick values spanning [min, max].
|
|
44
|
+
* @param min - Domain minimum
|
|
45
|
+
* @param max - Domain maximum
|
|
46
|
+
* @param targetCount - Desired number of ticks (approximate)
|
|
47
|
+
*/
|
|
48
|
+
export function computeNiceTicks(
|
|
49
|
+
min: number,
|
|
50
|
+
max: number,
|
|
51
|
+
targetCount: number,
|
|
52
|
+
): number[] {
|
|
53
|
+
if (targetCount < 1) {
|
|
54
|
+
targetCount = 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const range = niceNum(max - min, false);
|
|
58
|
+
const step = niceNum(range / targetCount, true);
|
|
59
|
+
const tickMin = Math.ceil(min / step) * step;
|
|
60
|
+
const tickMax = Math.floor(max / step) * step;
|
|
61
|
+
|
|
62
|
+
const ticks: number[] = [];
|
|
63
|
+
|
|
64
|
+
// Use epsilon to avoid floating point overshoot
|
|
65
|
+
for (let t = tickMin; t <= tickMax + step * 0.001; t += step) {
|
|
66
|
+
ticks.push(t);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return ticks;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format a numeric tick value for display.
|
|
74
|
+
* Uses K/M/B suffixes for large numbers, fixed decimals for small.
|
|
75
|
+
*/
|
|
76
|
+
export function formatTickValue(val: number): string {
|
|
77
|
+
const abs = Math.abs(val);
|
|
78
|
+
if (abs === 0) {
|
|
79
|
+
return "0";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (abs >= 1e9) {
|
|
83
|
+
return (val / 1e9).toFixed(1) + "B";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (abs >= 1e6) {
|
|
87
|
+
return (val / 1e6).toFixed(1) + "M";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (abs >= 1e3) {
|
|
91
|
+
return (val / 1e3).toFixed(1) + "K";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Number.isInteger(val)) {
|
|
95
|
+
return val.toString();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (abs >= 1) {
|
|
99
|
+
return val.toFixed(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return val.toFixed(2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Format a timestamp (ms since epoch) as a human-readable date/time label.
|
|
107
|
+
* Adapts precision based on the tick spacing.
|
|
108
|
+
*/
|
|
109
|
+
export function formatDateTickValue(val: number, stepMs?: number): string {
|
|
110
|
+
const d = new Date(val);
|
|
111
|
+
if (isNaN(d.getTime())) {
|
|
112
|
+
return formatTickValue(val);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// If step is provided, choose precision based on tick interval
|
|
116
|
+
if (stepMs !== undefined && stepMs > 0) {
|
|
117
|
+
const DAY = 86_400_000;
|
|
118
|
+
const HOUR = 3_600_000;
|
|
119
|
+
const MINUTE = 60_000;
|
|
120
|
+
|
|
121
|
+
if (stepMs >= DAY * 28) {
|
|
122
|
+
// Monthly or longer — show year-month
|
|
123
|
+
return d.toLocaleDateString(undefined, {
|
|
124
|
+
year: "numeric",
|
|
125
|
+
month: "short",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (stepMs >= DAY) {
|
|
130
|
+
// Daily — show month and day
|
|
131
|
+
return d.toLocaleDateString(undefined, {
|
|
132
|
+
month: "short",
|
|
133
|
+
day: "numeric",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (stepMs >= HOUR) {
|
|
138
|
+
// Hourly
|
|
139
|
+
return d.toLocaleString(undefined, {
|
|
140
|
+
month: "short",
|
|
141
|
+
day: "numeric",
|
|
142
|
+
hour: "numeric",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (stepMs >= MINUTE) {
|
|
147
|
+
// Minutes
|
|
148
|
+
return d.toLocaleTimeString(undefined, {
|
|
149
|
+
hour: "numeric",
|
|
150
|
+
minute: "2-digit",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Sub-minute
|
|
155
|
+
return d.toLocaleTimeString(undefined, {
|
|
156
|
+
hour: "numeric",
|
|
157
|
+
minute: "2-digit",
|
|
158
|
+
second: "2-digit",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Default: show date only
|
|
163
|
+
return d.toLocaleDateString(undefined, {
|
|
164
|
+
year: "numeric",
|
|
165
|
+
month: "short",
|
|
166
|
+
day: "numeric",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Web Mercator projection helpers and XYZ tile-pyramid math. Pure
|
|
15
|
+
* functions, no side effects, safe to call from any thread (including
|
|
16
|
+
* the renderer worker).
|
|
17
|
+
*
|
|
18
|
+
* Mercator output is in *meters*, not the normalized [0, 1] form that
|
|
19
|
+
* some libraries use. We feed the meter values straight into the
|
|
20
|
+
* cartesian projection matrix in `plot-layout.ts`, so the upstream
|
|
21
|
+
* domain stays in physical units and per-pixel ground resolution is a
|
|
22
|
+
* straightforward division.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* WGS84 equatorial radius in meters. Matches what every standard tile
|
|
27
|
+
* provider (OSM, CartoDB, Mapbox, ...) uses for Web Mercator.
|
|
28
|
+
*/
|
|
29
|
+
export const EARTH_RADIUS_M = 6378137;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Half the world extent in Mercator meters: π · R ≈ 20037508.34. The
|
|
33
|
+
* full Mercator square is `[-WORLD_HALF, +WORLD_HALF]` on both axes.
|
|
34
|
+
*/
|
|
35
|
+
export const WORLD_HALF = Math.PI * EARTH_RADIUS_M;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Maximum absolute latitude representable in Web Mercator. Beyond this
|
|
39
|
+
* the projection diverges to ±∞; tile providers don't ship tiles
|
|
40
|
+
* outside [-MAX_LAT, +MAX_LAT]. Computed as
|
|
41
|
+
* `atan(sinh(π)) · 180 / π ≈ 85.0511287798°`.
|
|
42
|
+
*/
|
|
43
|
+
export const MAX_LAT = 85.0511287798066;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Project (longitude, latitude) in degrees to Web Mercator meters.
|
|
47
|
+
*
|
|
48
|
+
* Latitudes outside ±MAX_LAT return `[NaN, NaN]` so callers in the
|
|
49
|
+
* cartesian build path (which already has a post-`projectPoint` NaN
|
|
50
|
+
* guard) discard those rows without special-casing.
|
|
51
|
+
*/
|
|
52
|
+
export function lonLatToMercator(lon: number, lat: number): [number, number] {
|
|
53
|
+
if (lat > MAX_LAT || lat < -MAX_LAT) {
|
|
54
|
+
return [NaN, NaN];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const x = (lon * Math.PI * EARTH_RADIUS_M) / 180;
|
|
58
|
+
const latRad = (lat * Math.PI) / 180;
|
|
59
|
+
const y = EARTH_RADIUS_M * Math.log(Math.tan(Math.PI / 4 + latRad / 2));
|
|
60
|
+
return [x, y];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Inverse: Mercator meters → (lon, lat) in degrees. Used by tooltip
|
|
65
|
+
* formatting and any UI that surfaces the cursor position to the user.
|
|
66
|
+
*/
|
|
67
|
+
export function mercatorToLonLat(x: number, y: number): [number, number] {
|
|
68
|
+
const lon = (x * 180) / (Math.PI * EARTH_RADIUS_M);
|
|
69
|
+
const lat = (Math.atan(Math.exp(y / EARTH_RADIUS_M)) * 360) / Math.PI - 90;
|
|
70
|
+
return [lon, lat];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A single XYZ tile address.
|
|
75
|
+
*/
|
|
76
|
+
export interface TileId {
|
|
77
|
+
z: number;
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Mercator extent in meters of one XYZ tile. Y is in the Mercator
|
|
84
|
+
* convention (north positive), not the tile-pyramid convention (y=0
|
|
85
|
+
* at the top); the conversion is done inside the helper.
|
|
86
|
+
*/
|
|
87
|
+
export interface TileExtent {
|
|
88
|
+
xMin: number;
|
|
89
|
+
yMin: number;
|
|
90
|
+
xMax: number;
|
|
91
|
+
yMax: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Return the Mercator-meter bounds of an XYZ tile. Tile (0, 0) at
|
|
96
|
+
* zoom 0 covers `[-WORLD_HALF, +WORLD_HALF]` on both axes — the whole
|
|
97
|
+
* world. Each zoom level subdivides into `2^z × 2^z` equal squares.
|
|
98
|
+
*/
|
|
99
|
+
export function tileExtent(z: number, x: number, y: number): TileExtent {
|
|
100
|
+
const n = 1 << z;
|
|
101
|
+
const tileSize = (2 * WORLD_HALF) / n;
|
|
102
|
+
const xMin = -WORLD_HALF + x * tileSize;
|
|
103
|
+
const xMax = xMin + tileSize;
|
|
104
|
+
// Tile y=0 sits at the *top* of the pyramid (north), so flip.
|
|
105
|
+
const yMax = WORLD_HALF - y * tileSize;
|
|
106
|
+
const yMin = yMax - tileSize;
|
|
107
|
+
return { xMin, yMin, xMax, yMax };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Pick the integer zoom level whose tile pixel resolution best matches
|
|
112
|
+
* the requested Mercator-meters-per-pixel. Snaps to the next coarser
|
|
113
|
+
* level so we never undersample (a finer level would fetch tiles only
|
|
114
|
+
* to scale them down).
|
|
115
|
+
*
|
|
116
|
+
* `targetResolutionMpp` is meters per *device pixel*; the caller
|
|
117
|
+
* computes it as `(domain.xMax - domain.xMin) / plotRect.width`.
|
|
118
|
+
*/
|
|
119
|
+
export function pickZoom(
|
|
120
|
+
targetResolutionMpp: number,
|
|
121
|
+
tileSizePx = 256,
|
|
122
|
+
maxZoom = 19,
|
|
123
|
+
): number {
|
|
124
|
+
if (!isFinite(targetResolutionMpp) || targetResolutionMpp <= 0) {
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Resolution at zoom z = (2·WORLD_HALF) / (tileSizePx · 2^z).
|
|
129
|
+
// Solve for z; floor so we stay at the next coarser level when in
|
|
130
|
+
// between two levels.
|
|
131
|
+
const z = Math.log2((2 * WORLD_HALF) / (tileSizePx * targetResolutionMpp));
|
|
132
|
+
return Math.max(0, Math.min(maxZoom, Math.floor(z)));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Enumerate every visible tile at a single zoom level that intersects
|
|
137
|
+
* the given Mercator extent. Returned in left-to-right, top-to-bottom
|
|
138
|
+
* order so the layer's render loop produces a deterministic draw
|
|
139
|
+
* sequence (helps with debugging tile-load races).
|
|
140
|
+
*
|
|
141
|
+
* Tiles outside the world's `[0, 2^z)` X range are *not* wrapped —
|
|
142
|
+
* antimeridian wraparound is a v2 feature. Callers see a gap when
|
|
143
|
+
* panning past ±180° lon; tiles inside the valid range still render.
|
|
144
|
+
*/
|
|
145
|
+
export function tilesForExtent(extent: TileExtent, z: number): TileId[] {
|
|
146
|
+
const n = 1 << z;
|
|
147
|
+
const tileSize = (2 * WORLD_HALF) / n;
|
|
148
|
+
|
|
149
|
+
// Tile X grows east; tile Y grows south. Convert the extent's
|
|
150
|
+
// bounds accordingly.
|
|
151
|
+
const xMinTile = Math.floor((extent.xMin + WORLD_HALF) / tileSize);
|
|
152
|
+
const xMaxTile = Math.floor((extent.xMax + WORLD_HALF) / tileSize);
|
|
153
|
+
const yMinTile = Math.floor((WORLD_HALF - extent.yMax) / tileSize);
|
|
154
|
+
const yMaxTile = Math.floor((WORLD_HALF - extent.yMin) / tileSize);
|
|
155
|
+
|
|
156
|
+
const result: TileId[] = [];
|
|
157
|
+
for (let ty = yMinTile; ty <= yMaxTile; ty++) {
|
|
158
|
+
if (ty < 0 || ty >= n) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (let tx = xMinTile; tx <= xMaxTile; tx++) {
|
|
163
|
+
if (tx < 0 || tx >= n) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
result.push({ z, x: tx, y: ty });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Return the parent tile (one zoom level coarser) of the given tile,
|
|
176
|
+
* along with the [0, 1] UV sub-rect that this tile occupies inside
|
|
177
|
+
* its parent. Used by the layer's "draw what we have" fallback while
|
|
178
|
+
* a missing target tile is in-flight — the parent texture is sampled
|
|
179
|
+
* with the sub-rect so the visible region keeps tile-aligned content
|
|
180
|
+
* instead of flashing blank.
|
|
181
|
+
*
|
|
182
|
+
* Returns `null` for zoom-0 tiles (no parent).
|
|
183
|
+
*/
|
|
184
|
+
export function parentTile(tile: TileId): {
|
|
185
|
+
parent: TileId;
|
|
186
|
+
uvMin: [number, number];
|
|
187
|
+
uvMax: [number, number];
|
|
188
|
+
} | null {
|
|
189
|
+
if (tile.z <= 0) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const parent: TileId = {
|
|
194
|
+
z: tile.z - 1,
|
|
195
|
+
x: tile.x >> 1,
|
|
196
|
+
y: tile.y >> 1,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const u = tile.x & 1;
|
|
200
|
+
const v = tile.y & 1;
|
|
201
|
+
const uvMin: [number, number] = [u * 0.5, v * 0.5];
|
|
202
|
+
const uvMax: [number, number] = [uvMin[0] + 0.5, uvMin[1] + 0.5];
|
|
203
|
+
return { parent, uvMin, uvMax };
|
|
204
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Bounded LRU of `WebGLTexture` keyed by tile cache key (see
|
|
15
|
+
* `tile-loader.tileKey`). Evicts least-recently-touched entries on
|
|
16
|
+
* insert past `capacity`, deleting their GPU textures.
|
|
17
|
+
*
|
|
18
|
+
* Default capacity of 256 is enough to cover a full screen worth of
|
|
19
|
+
* tiles at zoom-1 jumps either side of the current viewport plus the
|
|
20
|
+
* parent fallbacks — at 256×256 RGBA that's ~64 MB of texture memory,
|
|
21
|
+
* which sits comfortably under the 256 MB headroom most browsers
|
|
22
|
+
* grant to a tab.
|
|
23
|
+
*/
|
|
24
|
+
export class TileCache {
|
|
25
|
+
private _entries = new Map<string, WebGLTexture>();
|
|
26
|
+
private readonly _capacity: number;
|
|
27
|
+
|
|
28
|
+
constructor(capacity = 256) {
|
|
29
|
+
this._capacity = capacity;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Fetch a texture by key. Touching an existing entry moves it to
|
|
34
|
+
* the LRU tail so it survives the next eviction sweep.
|
|
35
|
+
*/
|
|
36
|
+
get(key: string): WebGLTexture | undefined {
|
|
37
|
+
const tex = this._entries.get(key);
|
|
38
|
+
if (tex !== undefined) {
|
|
39
|
+
// Re-insert to push the entry to the tail.
|
|
40
|
+
this._entries.delete(key);
|
|
41
|
+
this._entries.set(key, tex);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return tex;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Insert a texture under `key`. If the cache is at capacity, evict
|
|
49
|
+
* the oldest entry first (calling `gl.deleteTexture` on its GPU
|
|
50
|
+
* resource).
|
|
51
|
+
*/
|
|
52
|
+
set(
|
|
53
|
+
gl: WebGL2RenderingContext | WebGLRenderingContext,
|
|
54
|
+
key: string,
|
|
55
|
+
texture: WebGLTexture,
|
|
56
|
+
): void {
|
|
57
|
+
if (this._entries.has(key)) {
|
|
58
|
+
const old = this._entries.get(key)!;
|
|
59
|
+
gl.deleteTexture(old);
|
|
60
|
+
this._entries.delete(key);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._entries.set(key, texture);
|
|
64
|
+
while (this._entries.size > this._capacity) {
|
|
65
|
+
const oldestKey = this._entries.keys().next().value;
|
|
66
|
+
if (oldestKey === undefined) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const tex = this._entries.get(oldestKey)!;
|
|
71
|
+
gl.deleteTexture(tex);
|
|
72
|
+
this._entries.delete(oldestKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether a key is resident. Used to gate the "kick off a fetch"
|
|
78
|
+
* branch in the layer's render loop.
|
|
79
|
+
*/
|
|
80
|
+
has(key: string): boolean {
|
|
81
|
+
return this._entries.has(key);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Release every texture. Called on chart destroy. Safe to call
|
|
86
|
+
* with a stale `gl` reference (no-op if `deleteTexture` rejects),
|
|
87
|
+
* but in practice the caller passes the still-live worker context.
|
|
88
|
+
*/
|
|
89
|
+
dispose(gl: WebGL2RenderingContext | WebGLRenderingContext): void {
|
|
90
|
+
for (const tex of this._entries.values()) {
|
|
91
|
+
gl.deleteTexture(tex);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this._entries.clear();
|
|
95
|
+
}
|
|
96
|
+
}
|