diff --git a/.eslintrc b/.eslintrc
index 55b719926..cc67d8767 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -54,6 +54,7 @@
}
}
],
+ "typescript-eslint/no-unnecessary-type-assertion": "off",
"unicorn/consistent-destructuring": "off",
"unicorn/new-for-builtins": "off",
"unicorn/prevent-abbreviations": "off",
diff --git a/src/aesthetics/Aesthetic.ts b/src/aesthetics/Aesthetic.ts
index 8a8edc1de..63c2a24a1 100644
--- a/src/aesthetics/Aesthetic.ts
+++ b/src/aesthetics/Aesthetic.ts
@@ -1,7 +1,5 @@
import type { TextureSet } from './AestheticSet';
-import {
- isConstantChannel,
-} from '../typing';
+import { isConstantChannel } from '../typing';
import { Type, Vector } from 'apache-arrow';
import { StructRowProxy } from 'apache-arrow/row/struct';
import { isNumber } from 'lodash';
@@ -22,7 +20,7 @@ import { Scatterplot } from '../scatterplot';
export abstract class Aesthetic<
ChannelType extends DS.ChannelType,
Input extends DS.InType = DS.NumberIn,
- Output extends DS.OutType = DS.NumberOut
+ Output extends DS.OutType = DS.NumberOut,
> {
public abstract default_constant: Output['rangeType'];
public abstract default_range: [Output['rangeType'], Output['rangeType']];
@@ -31,8 +29,8 @@ export abstract class Aesthetic<
public _texture_buffer: Float32Array | Uint8Array | null = null;
protected abstract _func?: (d: Input['domainType']) => Output['rangeType'];
public aesthetic_map: TextureSet;
- public column : Vector | null;
-
+ public column: Vector | null = null;
+
// cache of the d3 scale
public encoding: ChannelType;
public id: string;
@@ -40,15 +38,14 @@ export abstract class Aesthetic<
encoding: ChannelType | null,
scatterplot: Scatterplot,
aesthetic_map: TextureSet,
- id: string
+ id: string,
) {
this.aesthetic_map = aesthetic_map;
if (this.aesthetic_map === undefined) {
-
throw new Error('Aesthetic map is undefined');
}
if (typeof this.aesthetic_map === 'function') {
- throw new Error("WTF")
+ throw new Error('WTF');
}
this.scatterplot = scatterplot;
@@ -57,7 +54,7 @@ export abstract class Aesthetic<
if (encoding === undefined) {
throw new Error(
- 'Updates with undefined should be handled upstream of the aesthetic.'
+ 'Updates with undefined should be handled upstream of the aesthetic.',
);
}
@@ -68,7 +65,7 @@ export abstract class Aesthetic<
if (isNumber(encoding)) {
throw new Error(
- `As of deepscatter 3.0, you must pass {constant: ${encoding}}, not just "${encoding}`
+ `As of deepscatter 3.0, you must pass {constant: ${encoding}}, not just "${encoding}`,
);
}
@@ -86,17 +83,17 @@ export abstract class Aesthetic<
}
abstract apply(point: Datum): Output['rangeType'];
-
+
abstract toGLType(val: Output['rangeType']): Output['glType'];
get webGLDomain() {
- console.log("No method for webGLDomain")
- return [0, 1] as [number, number]
+ console.log('No method for webGLDomain');
+ return [0, 1] as [number, number];
}
default_data(): Uint8Array | Float32Array | Array {
const default_value = this.toGLType(this.default_constant);
return Array(this.aesthetic_map.texture_size).fill(
- default_value
+ default_value,
) as Array;
}
@@ -118,7 +115,7 @@ export abstract class Aesthetic<
return this.aesthetic_map.get_position(this.id);
}
- get texture_buffer() : Uint8Array {
+ get texture_buffer(): Uint8Array {
if (this._texture_buffer) {
return this._texture_buffer as Uint8Array;
}
@@ -133,14 +130,14 @@ export abstract class Aesthetic<
arrow_column(): Vector | null {
if (this.column) {
- return this.column
+ return this.column;
}
if (this.field === null || this.field === undefined) {
- return this.column = null;
+ return (this.column = null);
}
- return this.column = this.dataset.root_tile.record_batch.getChild(this.field) as Vector<
- Input['arrowType']
- >;
+ return (this.column = this.dataset.root_tile.record_batch.getChild(
+ this.field,
+ ) as Vector);
}
is_dictionary(): boolean {
diff --git a/src/interaction.ts b/src/interaction.ts
index a6c09066d..9a975b788 100644
--- a/src/interaction.ts
+++ b/src/interaction.ts
@@ -1,4 +1,6 @@
/* eslint-disable no-underscore-dangle */
+/* eslint-disable @typescript-eslint/unbound-method */
+
import { select } from 'd3-selection';
import { timer } from 'd3-timer';
import { D3ZoomEvent, zoom, zoomIdentity } from 'd3-zoom';
@@ -78,7 +80,6 @@ export class Zoom {
.translate(width / 2, height / 2)
.scale(k)
.translate(-scales.x(x), -scales.y(y));
-
canvas.transition().duration(duration).call(zoomer.transform, t);
}
@@ -102,9 +103,9 @@ export class Zoom {
.style('background', 'ivory'),
(update) =>
update.html((d) =>
- this.scatterplot.tooltip_html(d.data, this.scatterplot)
+ this.scatterplot.tooltip_html(d.data, this.scatterplot),
),
- (exit) => exit.call((e) => e.remove())
+ (exit) => exit.call((e) => e.remove()),
);
els
@@ -150,11 +151,7 @@ export class Zoom {
[width, height],
])
.on('zoom', (event: D3ZoomEvent) => {
- try {
- document.getElementById('tooltipcircle').remove();
- } catch (error) {
- // console.log(error);
- }
+ document.getElementById('tooltipcircle')?.remove();
this.transform = event.transform;
this.restart_timer(10 * 1000);
@@ -179,19 +176,19 @@ export class Zoom {
const annotations: Annotation[] = data.map((d) => {
return {
- x: x_((xdim.apply(d))),
- y: y_((ydim.apply(d))),
- data: d,
- dx: 0,
- dy: 30,
- }
- })
+ x: x_(xdim.apply(d)),
+ y: y_(ydim.apply(d)),
+ data: d,
+ dx: 0,
+ dy: 30,
+ };
+ });
this.html_annotation(annotations);
const sel = this.svg_element_selection.select('#mousepoints');
sel
.selectAll('circle.label')
- .data(data, (d_ : StructRowProxy) => d_.ix as number) // Unique identifier to not remove existing.
+ .data(data, (d_: StructRowProxy) => d_.ix as number) // Unique identifier to not remove existing.
.join(
(enter) =>
enter
@@ -208,7 +205,7 @@ export class Zoom {
(exit) =>
exit.call((e) => {
e.remove();
- })
+ }),
)
.on('click', (ev, dd) => {
this.scatterplot.click_function(dd, this.scatterplot);
@@ -238,14 +235,16 @@ export class Zoom {
});
}
- current_corners(): Rectangle | undefined {
+ current_corners(): Rectangle {
// The corners of the current zoom transform, in data coordinates.
const { width, height } = this;
// Use the rescaled versions of the scales.
const scales = this.scales();
if (scales === undefined) {
- return;
+ throw new Error(
+ 'Attempting to get map view before scales have been created',
+ );
}
const { x_, y_ } = scales;
@@ -386,7 +385,7 @@ export class Zoom {
export function window_transform(
x_scale: ScaleLinear,
- y_scale: ScaleLinear
+ y_scale: ScaleLinear,
) {
// width and height are svg parameters; x and y scales project from the data x and y into the
// the webgl space.
diff --git a/src/label_rendering.ts b/src/label_rendering.ts
index 350e41f17..8fe83d627 100644
--- a/src/label_rendering.ts
+++ b/src/label_rendering.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
import type { GeoJsonObject, GeoJsonProperties } from 'geojson';
import { Renderer } from './rendering';
import { BBox, RBush3D } from 'rbush-3d';
@@ -7,13 +8,15 @@ import { select } from 'd3-selection';
import { drag } from 'd3-drag';
import type * as DS from './shared';
import { Color } from './aesthetics/ColorAesthetic';
+import type { Zoom } from './interaction';
+
const handler = drag();
function pixel_ratio(scatterplot: Scatterplot): number {
// pixelspace
- const [px1, px2] = scatterplot._zoom.scales().x.range() as [number, number];
+ const [px1, px2] = scatterplot._zoom!.scales().x.range() as [number, number];
// dataspace
- const [dx1, dx2] = scatterplot._zoom.scales().x.domain() as [number, number];
+ const [dx1, dx2] = scatterplot._zoom!.scales().x.domain() as [number, number];
const ratio = (px2 - px1) / (dx2 - dx1);
return ratio;
}
@@ -41,14 +44,18 @@ export class LabelMaker extends Renderer {
constructor(
scatterplot: Scatterplot,
id_raw: string,
- options: DS.LabelOptions = {}
+ options: DS.LabelOptions = {},
) {
- super(scatterplot.div.node() as HTMLDivElement, scatterplot._root, scatterplot);
+ super(
+ scatterplot.div!.node() as HTMLDivElement,
+ scatterplot.dataset,
+ scatterplot,
+ );
this.options = options;
- this.canvas = scatterplot.elements[2]
- .selectAll('canvas')
+ this.canvas = scatterplot
+ .elements![2].selectAll('canvas')
.node() as HTMLCanvasElement;
- const svg = scatterplot.elements[3].selectAll('svg').node() as SVGElement;
+ const svg = scatterplot.elements![3].selectAll('svg').node() as SVGElement;
const id = id_raw.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, '---');
const labgroup = svg.querySelectorAll(`#${id}`);
// eslint-disable-next-line unicorn/prefer-ternary
@@ -57,7 +64,7 @@ export class LabelMaker extends Renderer {
.select('#labelrects')
.append('g')
.attr('id', id)
- .node();
+ .node() as SVGGElement;
} else {
this.labelgroup = labgroup[1] as SVGGElement;
}
@@ -65,21 +72,21 @@ export class LabelMaker extends Renderer {
if (this.canvas === undefined) {
throw new Error('WTF?');
}
- this.ctx = this.canvas.getContext('2d');
+ this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
this.tree = new DepthTree(
this.ctx,
pixel_ratio(scatterplot),
0.5,
[0.5, 1e6],
- options.margin === undefined ? 30 : options.margin
+ options.margin === undefined ? 30 : options.margin,
);
/* this.tree.accessor = (x, y) => {
const f = scatterplot._zoom.scales();
return [f.x(x), f.y(y)];
};*/
- this.bind_zoom(scatterplot._renderer.zoom);
+ this.bind_zoom(scatterplot._renderer!.zoom!);
}
/**
@@ -128,7 +135,7 @@ export class LabelMaker extends Renderer {
public update(
featureset: GeoJSON.FeatureCollection,
label_key: string,
- size_key: string | undefined
+ size_key: string | undefined,
// color_key
) {
// Insert an entire feature collection all at once.
@@ -140,13 +147,17 @@ export class LabelMaker extends Renderer {
}
if (geometry.type === 'Point') {
// The size can be specified; if not, it defaults to 16pt.
- const size = (properties[size_key] as number) ?? 16;
+ const size =
+ size_key && properties[size_key]
+ ? (properties[size_key] as number)
+ : 16;
+
let label = '';
if (
properties[label_key] !== undefined &&
properties[label_key] !== null
) {
- label = `${properties[label_key]}`
+ label = `${properties[label_key]}`;
}
const p: RawPoint = {
x: geometry.coordinates[0] + Math.random() * 0.1,
@@ -193,7 +204,7 @@ export class LabelMaker extends Renderer {
enter
.append('rect')
.attr('class', 'labellbox')
- .style('opacity', RECT_DEFAULT_OPACITY)
+ .style('opacity', RECT_DEFAULT_OPACITY),
);
const Y_BUFFER = 5;
@@ -238,7 +249,7 @@ export class LabelMaker extends Renderer {
}
const exists =
(dim.scale.domain() as string[]).indexOf(
- datum.properties[dim.field] as string
+ datum.properties[dim.field] as string,
) > -1;
if (exists) {
//@ts-expect-error -- it's a string
@@ -279,30 +290,31 @@ export class LabelMaker extends Renderer {
.attr('class', 'labelbbox')
.attr(
'x',
- (d: P3d) => x_(d.data.x) - (d.data.pixel_width * this.tree.pixel_ratio) / 2
+ (d: P3d) =>
+ x_(d.data.x) - (d.data.pixel_width * this.tree.pixel_ratio) / 2,
)
.attr(
'y',
(d: P3d) =>
y_(d.data.y) -
(d.data.pixel_height * this.tree.pixel_ratio) / 2 -
- Y_BUFFER
+ Y_BUFFER,
)
- .attr('width', (d : P3d) => d.data.pixel_width * this.tree.pixel_ratio)
+ .attr('width', (d: P3d) => d.data.pixel_width * this.tree.pixel_ratio)
.attr('stroke', 'red')
.attr(
'height',
- (d: P3d) => d.data.pixel_height * this.tree.pixel_ratio + Y_BUFFER * 2
+ (d: P3d) => d.data.pixel_height * this.tree.pixel_ratio + Y_BUFFER * 2,
)
- .attr('display', (d : P3d) => {
- return d.data.properties.__display as string || 'inline';
+ .attr('display', (d: P3d) => {
+ return (d.data.properties.__display as string) || 'inline';
})
.on('mouseover', (event, d) => {
select(event.target).style('opacity', RECT_DEFAULT_OPACITY);
this.hovered = '' + d.minZ + d.minX;
event.stopPropagation();
})
- .on('mousemove', function (event : MouseEvent) {
+ .on('mousemove', function (event: MouseEvent) {
event.stopPropagation();
})
.on('click', (event, d: P3d) => {
@@ -319,7 +331,7 @@ export class LabelMaker extends Renderer {
d.data.x = x_.invert(event.x);
d.data.y = y_.invert(event.y);
});
- handler.on('end', (event, d : P3d) => {
+ handler.on('end', (event, d: P3d) => {
console.log({ text: d.data.text, x: d.data.x, y: d.data.y });
});
bboxes.call(handler);
@@ -430,7 +442,7 @@ class DepthTree extends RBush3D {
pixel_ratio: number,
scale_factor = 0.5,
zoom = [0.1, 1000],
- margin = 10 // in screen pixels
+ margin = 10, // in screen pixels
) {
// scale factor used to determine how quickly points scale.
// Not implemented.
@@ -537,7 +549,7 @@ class DepthTree extends RBush3D {
// The depth until which we're hidden; from min_depth (.1 ish) to max_depth(100 ish)
let hidden_until = -1;
// The node hiding this one.
- let hidden_by : P3d;
+ let hidden_by: P3d;
for (const overlapper of this.search(p3d)) {
// Find the most closely overlapping 3d block.
// Although the other ones will retain 3d blocks'
diff --git a/src/regl_rendering.ts b/src/regl_rendering.ts
index 0f3484c94..8d6048ea0 100644
--- a/src/regl_rendering.ts
+++ b/src/regl_rendering.ts
@@ -6,6 +6,7 @@ import wrapREGL, {
Buffer,
DrawCommand,
DrawConfig,
+ DefaultContext,
} from 'regl';
import { range, sum } from 'd3-array';
// import { contours } from 'd3-contour';
@@ -44,6 +45,7 @@ import {
import { Color } from './aesthetics/ColorAesthetic';
import { StatefulAesthetic } from './aesthetics/StatefulAesthetic';
import { Filter, Foreground } from './aesthetics/BooleanAesthetic';
+import { ZoomTransform } from 'd3-zoom';
// eslint-disable-next-line import/prefer-default-export
export class ReglRenderer extends Renderer {
public regl: Regl;
@@ -52,7 +54,6 @@ export class ReglRenderer extends Renderer {
private _buffers: MultipurposeBufferSet;
public _initializations: Promise;
public dataset: Dataset;
- public zoom?: Zoom;
public _zoom?: Zoom;
public most_recent_restart?: number;
public _default_webgl_scale?: number[];
@@ -133,14 +134,20 @@ export class ReglRenderer extends Renderer {
// Would be better cached per draw call.
this.allocate_aesthetic_buffers();
+ if (!this.zoom) {
+ throw new Error('Unable to draw before zoom state set up.');
+ }
+ if (!this.most_recent_restart)
+ throw new Error('Failed to populate restart');
const {
prefs,
aes_to_buffer_num,
buffer_num_to_variable,
variable_to_buffer_num,
} = this;
- const transform = this.zoom.transform;
- const colorScales = this.aes.dim('color');
+ const transform: ZoomTransform = this.zoom
+ .transform as unknown as ZoomTransform;
+ const colorScales = this.aes.dim('color') as StatefulAesthetic;
const [currentColor, lastColor] = [
colorScales.current,
colorScales.last,
@@ -172,9 +179,9 @@ export class ReglRenderer extends Renderer {
last_webgl_scale: this._webgl_scale_history[1],
use_scale_for_tiles: this._use_scale_to_download_tiles,
grid_mode: 0,
- buffer_num_to_variable,
- aes_to_buffer_num,
- variable_to_buffer_num,
+ buffer_num_to_variable: buffer_num_to_variable!,
+ aes_to_buffer_num: aes_to_buffer_num!,
+ variable_to_buffer_num: variable_to_buffer_num!,
color_picker_mode: 0, // whether to draw as a color picker.
position_interpolation: this.aes.position_interpolation,
zoom_matrix: [
@@ -297,8 +304,7 @@ export class ReglRenderer extends Renderer {
try {
this.render_all(props);
} catch (error) {
- console.warn('ERROR NOTED');
- this.reglframe.cancel();
+ this.reglframe!.cancel();
throw error;
}
}
@@ -665,16 +671,16 @@ export class ReglRenderer extends Renderer {
const { props } = this;
props.only_color = only_color;
- let v: number;
+ let v: number = -1;
this.fbos.contour.use(() => {
this.regl.clear({ color: [0, 0, 0, 0] });
// read onto the contour vals.
this.render_points(props);
- this.regl.read(this.contour_vals);
+ this.regl.read(this.contour_vals as Uint8Array);
// Could be done faster on the GPU itself.
// But would require writing to float textures, which
// can be hard.
- v = sum(this.contour_vals);
+ v = sum(this.contour_vals as Uint8Array);
});
return v;
}
@@ -757,29 +763,6 @@ export class ReglRenderer extends Renderer {
return point_as_int;
}
- /* blur(fbo) {
- var passes = [];
- var radii = [Math.round(
- Math.max(1, state.bloom.radius * pixelRatio / state.bloom.downsample))];
- for (var radius = nextPow2(radii[0]) / 2; radius >= 1; radius /= 2) {
- radii.push(radius);
- }
- radii.forEach(radius => {
- for (var pass = 0; pass < state.bloom.blur.passes; pass++) {
- passes.push({
- kernel: 13,
- src: bloomFbo[0],
- dst: bloomFbo[1],
- direction: [radius, 0]
- }, {
- kernel: 13,
- src: bloomFbo[1],
- dst: bloomFbo[0],
- direction: [0, radius]
- });
- }
- })
-} */
get fill_buffer() {
//
if (!this._fill_buffer) {
@@ -811,14 +794,14 @@ export class ReglRenderer extends Renderer {
this.regl.clear({ color: [0, 0, 0, 0] });
// read onto the contour vals.
this.render_points(props);
- this.regl.read(this.contour_vals);
+ this.regl.read(this.contour_vals!);
});
// 3-pass blur
this.blur(this.fbos.contour, this.fbos.ping, 3);
this.fbos.contour.use(() => {
- this.regl.read(this.contour_vals);
+ this.regl.read(this.contour_vals!);
});
let i = 0;
@@ -834,6 +817,7 @@ export class ReglRenderer extends Renderer {
const { regl } = this;
// This should be scoped somewhere to allow resizing.
type P = DS.TileDrawProps;
+ type C = DefaultContext;
const parameters: DrawConfig = {
depth: { enable: false },
stencil: { enable: false },
@@ -862,7 +846,7 @@ export class ReglRenderer extends Renderer {
u_transition_duration(_, props: P) {
return props.prefs.duration; // Using seconds, not milliseconds, in there
},
- u_only_color(_, props: P) {
+ u_only_color(_: C, props: P) {
if (props.only_color !== undefined) {
return props.only_color;
}
@@ -871,7 +855,7 @@ export class ReglRenderer extends Renderer {
// Other values plot a specific value of the color-encoded field.
return -2;
},
- u_wrap_colors_after: (_, { wrap_colors_after }: P) => {
+ u_wrap_colors_after: (_: unknown, { wrap_colors_after }: P) => {
if (wrap_colors_after === undefined) {
throw new Error('wrap_colors_after is undefined');
}
@@ -893,7 +877,7 @@ export class ReglRenderer extends Renderer {
}
return 0;
},
- u_grid_mode: (_, { grid_mode }: P) => grid_mode,
+ u_grid_mode: (_: C, { grid_mode }: P) => grid_mode,
u_colors_as_grid: (_, { colors_as_grid }: P) => colors_as_grid,
/* u_constant_color: () => (this.aes.dim("color").current.constant !== undefined
? this.aes.dim("color").current.constant
@@ -901,19 +885,19 @@ export class ReglRenderer extends Renderer {
u_constant_last_color: () => (this.aes.dim("color").last.constant !== undefined
? this.aes.dim("color").last.constant
: [-1, -1, -1]),*/
- u_tile_id: (_, props: P) => props.tile_id,
- u_width: ({ viewportWidth }) => viewportWidth as number,
- u_height: ({ viewportHeight }) => viewportHeight as number,
+ u_tile_id: (_: C, props: P) => props.tile_id,
+ u_width: ({ viewportWidth }: C) => viewportWidth,
+ u_height: ({ viewportHeight }: C) => viewportHeight,
u_one_d_aesthetic_map: this.aes.aesthetic_map.one_d_texture,
u_color_aesthetic_map: this.aes.aesthetic_map.color_texture,
- u_aspect_ratio: ({ viewportWidth, viewportHeight }) =>
+ u_aspect_ratio: ({ viewportWidth, viewportHeight }: C) =>
viewportWidth / viewportHeight,
//@ts-expect-error Don't know about regl props.
u_zoom_balance: regl.prop('zoom_balance'),
- u_base_size: (_, { point_size }) => point_size as number,
- u_maxix: (_, { max_ix }) => max_ix as number,
- u_alpha: (_, { alpha }) => alpha as number,
- u_foreground_number: (_, { foreground }) => foreground as number,
+ u_base_size: (_: C, { point_size }: P) => point_size,
+ u_maxix: (_: C, { max_ix }: P) => max_ix,
+ u_alpha: (_: C, { alpha }: P) => alpha,
+ u_foreground_number: (_: C, { foreground }: P) => foreground as number,
u_foreground_alpha: () => this.render_props.foreground_opacity,
u_background_rgba: () => {
const color = this.prefs.background_options.color;
@@ -929,7 +913,7 @@ export class ReglRenderer extends Renderer {
this.prefs.background_options.mouseover ? 1 : 0,
u_background_size: () => this.render_props.background_size,
u_foreground_size: () => this.render_props.foreground_size,
- u_k: (_, props: P) => {
+ u_k: (_: DefaultContext, props: P) => {
return props.transform.k;
},
// Allow interpolation between different coordinate systems.
@@ -940,7 +924,7 @@ export class ReglRenderer extends Renderer {
u_time: ({ time }: P) => time,
u_jitter: () => this.aes.jitter_int_format('current'),
u_last_jitter: () => this.aes.jitter_int_format('last'),
- u_zoom(_, props: P) {
+ u_zoom(_: C, props: P) {
return props.zoom_matrix;
},
},
@@ -1285,10 +1269,7 @@ export class TileBufferManager {
if (!column.type || column.type.typeId !== Type.Float32) {
const buffer = new Float32Array(tile.record_batch.numRows);
const source_buffer = column.data[0];
-
- if (column.type['dictionary']) {
- // We set the dictionary values down by 2047 so that we can use
- // even half-precision floats for direct indexing.
+ if (column.type.typeId === Type.Dictionary) {
for (let i = 0; i < tile.record_batch.numRows; i++) {
buffer[i] = (source_buffer as Data>).values[i];
}
diff --git a/src/rendering.ts b/src/rendering.ts
index 30395c2da..2244357dd 100644
--- a/src/rendering.ts
+++ b/src/rendering.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
/* eslint-disable no-underscore-dangle */
import { BaseType, select } from 'd3-selection';
import { min } from 'd3-array';
@@ -130,7 +131,6 @@ export class Renderer {
// The renderer handles periodic dispatches of calls
public deferred_functions: Array<() => Promise | void>;
public _use_scale_to_download_tiles = true;
- public zoom?: Zoom;
public aes?: AestheticSet;
public _zoom?: Zoom;
public render_props: RenderProps = new RenderProps();
@@ -142,7 +142,7 @@ export class Renderer {
this.scatterplot = scatterplot;
this.holder = select(selector as string);
this.canvas = select(
- this.holder.node().firstElementChild,
+ this.holder!.node()!.firstElementChild,
).node() as HTMLCanvasElement;
this.dataset = tileSet;
this.width = +select(this.canvas).attr('width');
@@ -191,8 +191,7 @@ export class Renderer {
const total_intended_points = min([
max_ix,
this.dataset.highest_known_ix || 1e10,
- ]);
-
+ ]) as number;
const total_points = total_intended_points * (1 - discard_share);
const size_adjust = Math.exp(Math.log(k) * zoom_balance);
@@ -217,7 +216,7 @@ export class Renderer {
if (!this._use_scale_to_download_tiles) {
return max_points;
}
- const k = this.zoom.transform.k;
+ const k = this.zoom.transform!.k;
const point_size_adjust = Math.exp(Math.log(k) * prefs.zoom_balance);
return (max_points * k * k) / point_size_adjust / point_size_adjust;
}
@@ -229,6 +228,7 @@ export class Renderer {
const { dataset: tileSet } = this;
// Materialize using a tileset method.
+ if (!this.aes) throw new Error('Aesthetic missing');
const x = this.aes.dim('x') as StatefulAesthetic;
const y = this.aes.dim('x') as StatefulAesthetic;
const natural_display =
@@ -252,8 +252,13 @@ export class Renderer {
return all_tiles;
}
+ get zoom(): Zoom {
+ if (this._zoom === undefined) throw new Error('Zoom state not yet bound');
+ return this._zoom as Zoom;
+ }
+
bind_zoom(zoom: Zoom) {
- this.zoom = zoom;
+ this._zoom = zoom;
return this;
}
diff --git a/src/scatterplot.ts b/src/scatterplot.ts
index f7d948808..8e04c3dc0 100644
--- a/src/scatterplot.ts
+++ b/src/scatterplot.ts
@@ -207,6 +207,9 @@ export class Scatterplot {
duration = this.prefs.duration,
): Promise {
const selection = await this.select_data(params);
+ if (selection === null) {
+ throw new Error(`Invalid selection: ${JSON.stringify(params)}`);
+ }
await selection.ready;
await this.plotAPI({
duration,
@@ -268,7 +271,11 @@ export class Scatterplot {
* **or** a keyed of values like `{'Rome': 3, 'Vienna': 13}` in which case the numeric values will be used.
* @param key_field The field in which to look for the identifiers.
*/
- join(name: string, codes: Record, key_field: string) {
+ join(
+ name: string,
+ codes: Record | string[],
+ key_field: string,
+ ) {
let true_codes: Record;
if (Array.isArray(codes)) {
@@ -276,8 +283,10 @@ export class Scatterplot {
codes.map((next: string | bigint) => [String(next), 1]),
);
} else {
- this._root.add_label_identifiers(true_codes, name, key_field);
+ true_codes = codes;
}
+
+ this.dataset.add_label_identifiers(true_codes, name, key_field);
}
async add_labels_from_url(
@@ -412,7 +421,7 @@ export class Scatterplot {
HTMLDivElement,
HTMLCanvasElement
>;
- const ctx = bkgd.node().getContext('2d');
+ const ctx = bkgd.node()!.getContext('2d');
if (ctx === null) throw new Error("Can't acquire canvas context");
ctx.fillStyle = prefs.background_color ?? 'rgba(133, 133, 111, .8)';
@@ -460,18 +469,18 @@ export class Scatterplot {
* loaded tiles. Useful for debugging and illustration.
*/
- const canvas = this.elements[2]
- .selectAll('canvas')
- .node() as HTMLCanvasElement;
+ const canvas = this.elements![2].selectAll(
+ 'canvas',
+ ).node() as HTMLCanvasElement;
- const ctx = canvas.getContext('2d');
+ const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
// as CanvasRenderingContext2D;
ctx.clearRect(0, 0, 10_000, 10_000);
- const { x_, y_ } = this._zoom.scales();
+ const { x_, y_ } = this._zoom!.scales();
ctx.strokeStyle = '#888888';
- const tiles = this._root.map((t) => t);
+ const tiles = this.dataset.map((t) => t);
for (const i of range(20)) {
setTimeout(() => {
for (const tile of tiles) {
@@ -507,16 +516,18 @@ export class Scatterplot {
this._renderer?.regl?.destroy();
const node = this.div?.node() as Node;
- node.parentElement.replaceChildren();
+ node.parentElement!.replaceChildren();
}
update_prefs(prefs: DS.APICall) {
// Stash the previous values for interpolation.
if (this.prefs.encoding && prefs.encoding) {
- for (const k of Object.keys(this.prefs.encoding)) {
+ for (const k of Object.keys(
+ this.prefs.encoding,
+ ) as (keyof DS.Encoding)[]) {
if (prefs.encoding[k] !== undefined) {
- this.prefs.encoding[k] = prefs.encoding[k] as DS.Encoding;
+ this.prefs.encoding[k] = prefs.encoding[k];
}
}
}
@@ -553,7 +564,7 @@ export class Scatterplot {
if (v && v['label_key'] !== undefined) {
(this.secondary_renderers[k] as LabelMaker).stop();
(this.secondary_renderers[k] as LabelMaker).delete();
- this.secondary_renderers[k] = undefined;
+ delete this.secondary_renderers[k];
}
}
}
@@ -567,7 +578,7 @@ export class Scatterplot {
*/
public dim(dimension: DS.Dimension): ConcreteAesthetic {
- return this._renderer.aes.dim(dimension).current;
+ return this._renderer!.aes.dim(dimension)!.current;
}
set tooltip_html(func) {
@@ -769,32 +780,33 @@ export class Scatterplot {
if (this._zoom === undefined) {
await this.reinitialize();
}
-
- this._renderer.render_props.apply_prefs(this.prefs);
+ const renderer = this._renderer as ReglRenderer;
+ const zoom = this._zoom as Zoom;
+ this._renderer!.render_props.apply_prefs(this.prefs);
const { width, height } = this;
this.update_prefs(prefs);
if (prefs.zoom !== undefined) {
if (prefs.zoom === null) {
- this._zoom.zoom_to(1, width / 2, height / 2);
+ zoom.zoom_to(1, width / 2, height / 2);
prefs.zoom = undefined;
} else if (prefs.zoom?.bbox) {
- this._zoom.zoom_to_bbox(prefs.zoom.bbox, prefs.duration);
+ zoom.zoom_to_bbox(prefs.zoom.bbox, prefs.duration);
}
}
- this._renderer.most_recent_restart = Date.now();
- this._renderer.aes.apply_encoding(prefs.encoding ?? {});
+ renderer.most_recent_restart = Date.now();
+ renderer.aes.apply_encoding(prefs.encoding ?? {});
- if (this._renderer.reglframe) {
- const r = this._renderer.reglframe;
+ if (renderer.reglframe) {
+ const r = renderer.reglframe;
r.cancel();
- this._renderer.reglframe = undefined;
+ renderer.reglframe = undefined;
}
- this._renderer.reglframe = this._renderer.regl.frame(() => {
- this._renderer.tick();
+ renderer.reglframe = renderer.regl.frame(() => {
+ renderer.tick();
});
if (prefs.labels !== undefined) {
@@ -827,7 +839,7 @@ export class Scatterplot {
}
}
- this._zoom.restart_timer(60_000);
+ zoom.restart_timer(60_000);
}
get root_batch() {
@@ -843,13 +855,27 @@ export class Scatterplot {
*/
get query(): DS.APICall {
const p = JSON.parse(JSON.stringify(this.prefs)) as DS.APICall;
- p.zoom = { bbox: this._renderer.zoom.current_corners() };
+ p.zoom = { bbox: this.renderer.zoom.current_corners() };
return p;
}
+ get renderer(): ReglRenderer {
+ if (this._renderer === undefined) {
+ throw new Error('No renderer has been initialized');
+ }
+ return this._renderer;
+ }
+
+ get zoom(): Zoom {
+ if (this._zoom === undefined) {
+ throw new Error('No zoom has been initialized');
+ }
+ return this._zoom;
+ }
+
sample_points(n = 10): Record[] {
const vals: Record[] = [];
- for (const p of this._root.points(this._zoom.current_corners())) {
+ for (const p of this.dataset.points(this.zoom.current_corners())) {
vals.push({ ...p });
if (vals.length >= n * 3) {
break;
@@ -893,7 +919,7 @@ class LabelClick extends SettableFunction {
default(
feature: GeoJsonProperties,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- plot: Scatterplot = undefined,
+ plot: Scatterplot | undefined = undefined,
labelset: LabelMaker | undefined = undefined,
) {
let filter: DS.LambdaChannel | null;
@@ -931,14 +957,14 @@ class ChangeToHighlitPointFunction extends SettableFunction<
StructRowProxy[]
> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- default(points: StructRowProxy[], plot: Scatterplot = undefined) {
+ default(points: StructRowProxy[], plot: Scatterplot | undefined = undefined) {
return;
}
}
class TooltipHTML extends SettableFunction {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- default(point: StructRowProxy, plot: Scatterplot = undefined) {
+ default(point: StructRowProxy, plot: Scatterplot | undefined = undefined) {
// By default, this returns a
let output = '';
const nope: Set = new Set([
diff --git a/src/utilityFunctions.ts b/src/utilityFunctions.ts
index 63c0a7f53..32eb14e09 100644
--- a/src/utilityFunctions.ts
+++ b/src/utilityFunctions.ts
@@ -9,7 +9,7 @@ import {
makeVector,
} from 'apache-arrow';
-type IndicesType = null | Int8Array | Int16Array | Int32Array;
+type IndicesType = Int8Array | Int16Array | Int32Array;
type DictionaryType = Dictionary;
// We need to keep track of the current dictionary number
@@ -82,21 +82,3 @@ function createDictionaryWithVector(
return returnval;
}
-
-export function LazyGetter() {
- return function (target: any, key: string, descriptor: PropertyDescriptor) {
- const originalMethod = descriptor.get;
-
- descriptor.get = function () {
- const cacheKey = `_____${key}`;
-
- if (!this[cacheKey]) {
- this[cacheKey] = originalMethod.call(this);
- }
-
- return this[cacheKey];
- };
-
- return descriptor;
- };
-}
diff --git a/tsconfig.json b/tsconfig.json
index a2293466a..984330cd8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,7 +14,7 @@
"noUnusedLocals": true,
"experimentalDecorators": true,
"sourceMap": true,
- "strict": false,
+ "strict": true,
"target": "es2020",
"isolatedModules": true,
"outDir": "./dist",