@mosaicajs/deck 0.1.0-rc.4 → 0.1.0-rc.5

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.
@@ -2,19 +2,19 @@ import { WORLD_SIZE } from '@mosaicajs/client';
2
2
  import { worldBbox } from '@mosaicajs/client/internal';
3
3
  import { densityCellsOf, densityOfTiles } from './density.js';
4
4
  /**
5
- * The cell sizes on screen density's resolution is chosen from, in CSS pixels, coarse to fine.
5
+ * The cell sizes on screen that density can be drawn at, in CSS pixels, from coarse to fine.
6
6
  *
7
7
  * @category Colour
8
8
  */
9
9
  export const DENSITY_CELL_SIZES = [32, 24, 16, 12, 8, 6, 4];
10
10
  /**
11
- * The cell size density is drawn at until one is chosen, in CSS pixels.
11
+ * The cell size density is drawn at by default, in CSS pixels.
12
12
  *
13
13
  * @category Colour
14
14
  */
15
15
  export const DEFAULT_DENSITY_CELL_PX = 12;
16
16
  /**
17
- * How long the camera rests before {@link DensityCounter} asks for counts, in milliseconds.
17
+ * How long the camera must rest before {@link DensityCounter} fetches counts, in milliseconds.
18
18
  *
19
19
  * @category Colour
20
20
  */
@@ -34,8 +34,8 @@ const RINGS = 2;
34
34
  /** The deepest depth the aggregate route counts, the stored position's. */
35
35
  const DEEPEST = 32;
36
36
  /**
37
- * The depth whose cells are closest to `cellPx` across on screen at `zoom`, nearest on a log scale.
38
- * A cell at depth `d` is `WORLD_SIZE · 2^zoom / 2^d` pixels wide.
37
+ * The depth whose cells are closest to `cellPx` wide on screen at `zoom`, measured on a log scale.
38
+ * A cell at depth `d` is `512 · 2^zoom / 2^d` pixels wide.
39
39
  *
40
40
  * @category Colour
41
41
  */
@@ -68,8 +68,8 @@ function areaOf(camera, margin) {
68
68
  return worldBbox({ target: [camera.target[0] ?? 0, camera.target[1] ?? 0], zoom: camera.zoom, width: camera.width, height: camera.height }, margin);
69
69
  }
70
70
  /**
71
- * The stops of {@link DENSITY_CELL_SIZES} at `camera`, each with the depth it asks for and whether
72
- * that depth fits `limit` cells over the area asked for.
71
+ * Each size in {@link DENSITY_CELL_SIZES} at `camera`, with its depth and whether its cells over
72
+ * the area fetched number at most `limit`, the server's `selection.maxAggregateCells`.
73
73
  *
74
74
  * @category Colour
75
75
  */
@@ -92,7 +92,7 @@ function densityDepth(camera, cellPx, limit) {
92
92
  return Math.min(cellDepth(camera.zoom, nearestStop(cellPx)), deepest);
93
93
  }
94
94
  /**
95
- * The stop of {@link DENSITY_CELL_SIZES} nearest `px` on a log scale.
95
+ * The size in {@link DENSITY_CELL_SIZES} nearest `px`, measured on a log scale.
96
96
  *
97
97
  * @category Colour
98
98
  */
@@ -126,33 +126,22 @@ function levelsAt(camera, depth, limit, groupings) {
126
126
  return levels;
127
127
  }
128
128
  /**
129
- * Keeps the counts density is drawn from for a camera: one `POST /v1/aggregate` grouping of cells,
130
- * registered with the store, which sends its filters and its highlight with it and asks again when
131
- * either changes. The counts are the highlighted items' under a highlight and the filtered items'
132
- * otherwise.
129
+ * Fetches the counts that density is drawn from, for the area around a camera. It registers an
130
+ * aggregate with the store, so the counts cover only what the viewer may see under the store's
131
+ * filters, and it fetches again when the filters, the highlight or the store's view (coordinate
132
+ * system) change. While a
133
+ * highlight is set, it counts the highlighted items.
133
134
  *
134
- * A camera change asks nothing while the camera moves. {@link DENSITY_SETTLE_MS} after the last
135
- * one, the counter works out the depth whose cells are nearest the chosen size on screen and asks
136
- * for that depth over the viewport and a margin, unless the counts asked for last are at that depth
137
- * and their area holds the viewport. The same request asks for up to {@link RINGS} coarser levels,
138
- * each a depth coarser over twice the reach, as the points' ring is fetched coarser further out. A
139
- * move that leaves the finest area asked for drops the registration at once, so a filter changed
140
- * during the move asks nothing for an area no longer shown. The counts held are drawn at their own
141
- * world positions until the next answer lands, so a zoom scales them with the map and a pan within
142
- * the margin asks nothing: the finest level held whose area holds the viewport is drawn, so a zoom
143
- * out draws the coarser levels' counts to the edges, and where no level holds it the widest is
144
- * drawn. A view switch asks again at once.
135
+ * Tell it where the camera is with `look` and what to draw with `set`. Once the camera has rested
136
+ * for {@link DENSITY_SETTLE_MS}, it fetches cells of about the chosen size on screen over the
137
+ * viewport and a margin round it, so a short pan needs no new request. Until the first answer for
138
+ * the view arrives, or after the server refuses one, `counts` returns the coarser counts that come
139
+ * with the map's tiles. After that it returns the last answer until the next one arrives.
140
+ * `onChange` is called whenever the counts to draw change.
145
141
  *
146
- * A depth whose cells over the area would pass `selection.maxAggregateCells` is never asked for.
147
- * Where the server refuses a request with `422` anyway, the counter asks one depth coarser, up to
148
- * three times. After any other refusal it drops the answer it held, so density is drawn from the
149
- * frame's tile counts as below, and it asks again at the camera's next rest.
150
- *
151
- * Until an answer is held for the view, density is drawn from the counts the frame's tiles carry,
152
- * which land with each viewport response ahead of its points. They count the same items, at the
153
- * tiles' coarser cells, and are not drawn once an answer is held.
154
- *
155
- * `onChange` is called when the counts to draw change.
142
+ * A {@link MosaicaLayer} with a `store` keeps a counter of its own. Make one yourself and pass it
143
+ * as the layer's `densityCounter` if your own code also needs the counts, as `<mosaica-map>` does
144
+ * for its density key.
156
145
  *
157
146
  * @category Colour
158
147
  */
@@ -173,6 +162,11 @@ export class DensityCounter {
173
162
  /** The tiles projection last read, and the counts derived from it. */
174
163
  tiles = null;
175
164
  unsubscribe;
165
+ /**
166
+ * @param store - The store to fetch through.
167
+ * @param onChange - Called whenever the counts to draw change.
168
+ * @param settleMs - How long the camera must rest before counts are fetched, in milliseconds.
169
+ */
176
170
  constructor(store, onChange, settleMs = DENSITY_SETTLE_MS) {
177
171
  this.store = store;
178
172
  this.onChange = onChange;
@@ -182,7 +176,7 @@ export class DensityCounter {
182
176
  this.meta = store.get('meta');
183
177
  this.unsubscribe = store.subscribe(() => this.read());
184
178
  }
185
- /** The camera moved or the canvas was resized. Counts are asked for once it has rested. */
179
+ /** Tell the counter the camera has moved or the canvas has been resized. */
186
180
  look(camera) {
187
181
  const drawn = this.answered();
188
182
  this.camera = camera;
@@ -194,7 +188,7 @@ export class DensityCounter {
194
188
  this.onChange();
195
189
  this.schedule();
196
190
  }
197
- /** What to draw. Off drops the registration and the counts held at once. */
191
+ /** Turn density on or off and choose the cell size. Turning it off drops the counts held. */
198
192
  set(settings) {
199
193
  const was = this.settings;
200
194
  this.settings = settings;
@@ -206,8 +200,8 @@ export class DensityCounter {
206
200
  this.schedule();
207
201
  }
208
202
  /**
209
- * The counts to draw: the last answer, while it was counted in the store's current view, and
210
- * until there is one the frame's tile counts. Nothing while density is off.
203
+ * The counts to draw, or `null` while density is off or there are none. Until the first answer
204
+ * for the current view arrives, these are the coarser counts that come with the map's tiles.
211
205
  */
212
206
  counts() {
213
207
  if (!this.settings.on)
@@ -229,14 +223,17 @@ export class DensityCounter {
229
223
  this.tiles = { tiles, counts: densityOfTiles(tiles, this.store.get('view').depth) };
230
224
  return this.tiles.counts.cells.length > 0 ? this.tiles.counts : null;
231
225
  }
232
- /** The resolution stops at the camera as it stands; every stop is enabled before a camera and `meta`. */
226
+ /**
227
+ * The cell sizes density can be drawn at for the current camera. Every stop is enabled until the
228
+ * counter has a camera and the store has `meta`.
229
+ */
233
230
  stops() {
234
231
  const meta = this.store.get('meta');
235
232
  if (!this.camera || !meta)
236
233
  return DENSITY_CELL_SIZES.map((px) => ({ px, depth: 0, enabled: true }));
237
234
  return resolutionStops(this.camera, meta.selection.maxAggregateCells);
238
235
  }
239
- /** Drop the registration and stop following the store. */
236
+ /** Stop fetching counts and unsubscribe from the store. */
240
237
  dispose() {
241
238
  this.stop();
242
239
  this.unsubscribe();
package/dist/density.d.ts CHANGED
@@ -21,37 +21,37 @@ import { type Rgb } from './colour.js';
21
21
  * {@link filterDensity} smooths the binned image so the cells do not show as hard-edged squares.
22
22
  */
23
23
  /**
24
- * How density is drawn: `none`; `smooth`, a soft wash; `hex`, hexagonal bins; `grid`, square bins
25
- * one cell wide; or `contours`, lines of equal density.
24
+ * How density is drawn: `none`, `smooth` (a soft wash), `hex` (hexagonal bins), `grid` (square
25
+ * cells) or `contours` (lines of equal density).
26
26
  *
27
27
  * @category Colour
28
28
  */
29
29
  export type DensityMode = 'none' | 'smooth' | 'hex' | 'grid' | 'contours';
30
30
  /**
31
- * The colours density is drawn in: `warm-grey`, one neutral hue whose strength follows the count,
32
- * or a ramp (`viridis`, `cividis`, `magma`, `greys`) whose dense end contrasts with the ground, so
33
- * dense ground is dark on a light map and bright on a dark one.
31
+ * The colours density is drawn in: `warm-grey`, one neutral colour that strengthens with the
32
+ * count, or one of the ramps `viridis`, `cividis`, `magma` and `greys`. A ramp runs whichever way
33
+ * makes dense areas dark on a light map and bright on a dark one.
34
34
  *
35
35
  * @category Colour
36
36
  */
37
37
  export type DensityColours = 'warm-grey' | 'viridis' | 'cividis' | 'magma' | 'greys';
38
38
  /**
39
- * How a count is placed between no items and the largest count drawn: `linear`, in proportion to
40
- * the count, or `log`, in proportion to the logarithm of one more than the count, which spreads
41
- * counts that span several orders of magnitude.
39
+ * How a cell's count is placed between zero and the largest count drawn: `linear`, in proportion
40
+ * to the count, or `log`, in proportion to `log(1 + count)`, which suits counts that span several
41
+ * orders of magnitude.
42
42
  *
43
43
  * @category Colour
44
44
  */
45
45
  export type DensityScale = 'linear' | 'log';
46
- /** The density scale used where none is set. */
46
+ /** The density scale used when none is set: `log`. */
47
47
  export declare const DEFAULT_DENSITY_SCALE: DensityScale;
48
48
  /**
49
- * Where `count` sits on `scale`, from 0 at no items to 1 at `max`: `count / max` under `linear` and
50
- * `log1p(count) / log1p(max)` under `log`. A count is clamped to `[0, max]`, and every count is at
51
- * 0 where `max` is 0 or less.
49
+ * Where `count` falls on `scale`, from 0 for no items to 1 for `max`: `count / max` under `linear`,
50
+ * and `log1p(count) / log1p(max)` under `log`. The count is clamped to `[0, max]`, and the result
51
+ * is 0 if `max` is 0 or less.
52
52
  */
53
53
  export declare function densityPosition(count: number, max: number, scale: DensityScale): number;
54
- /** The count at `position` on `scale` up to `max`: the inverse of {@link densityPosition}. */
54
+ /** The count at `position` on `scale`, the inverse of {@link densityPosition}. Use it to label a density key. */
55
55
  export declare function densityCountAt(position: number, max: number, scale: DensityScale): number;
56
56
  /** The largest count among `cells`, 0 where there are none. */
57
57
  export declare function maxCount(cells: readonly {
@@ -93,15 +93,16 @@ export type DensityCell = {
93
93
  count: number;
94
94
  };
95
95
  /**
96
- * The counts density is drawn from: one {@link DensityCell} per non-empty cell at `depth`, where a
97
- * cell at depth `d` is one of `2^d` by `2^d` over the view, as a tile is at depths up to 16.
96
+ * Counts of items by cell, which density is drawn from. At depth `d` the view's square is divided
97
+ * into `2^d` by `2^d` cells, so each depth halves the cell's width. Only cells with a count are
98
+ * listed.
98
99
  *
99
100
  * @category Colour
100
101
  */
101
102
  export type DensityCounts = {
102
103
  /** The depth the cells are at. */
103
104
  depth: number;
104
- /** The cells with a count, in any order. */
105
+ /** The cells, in any order. */
105
106
  cells: readonly DensityCell[];
106
107
  };
107
108
  /**
package/dist/density.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { WORLD_SIZE } from '@mosaicajs/client';
2
2
  import { RAMPS, rampAt } from './colour.js';
3
- /** The density scale used where none is set. */
3
+ /** The density scale used when none is set: `log`. */
4
4
  export const DEFAULT_DENSITY_SCALE = 'log';
5
5
  /**
6
- * Where `count` sits on `scale`, from 0 at no items to 1 at `max`: `count / max` under `linear` and
7
- * `log1p(count) / log1p(max)` under `log`. A count is clamped to `[0, max]`, and every count is at
8
- * 0 where `max` is 0 or less.
6
+ * Where `count` falls on `scale`, from 0 for no items to 1 for `max`: `count / max` under `linear`,
7
+ * and `log1p(count) / log1p(max)` under `log`. The count is clamped to `[0, max]`, and the result
8
+ * is 0 if `max` is 0 or less.
9
9
  */
10
10
  export function densityPosition(count, max, scale) {
11
11
  if (!(max > 0))
@@ -13,7 +13,7 @@ export function densityPosition(count, max, scale) {
13
13
  const c = Math.min(max, Math.max(0, count));
14
14
  return scale === 'log' ? Math.log1p(c) / Math.log1p(max) : c / max;
15
15
  }
16
- /** The count at `position` on `scale` up to `max`: the inverse of {@link densityPosition}. */
16
+ /** The count at `position` on `scale`, the inverse of {@link densityPosition}. Use it to label a density key. */
17
17
  export function densityCountAt(position, max, scale) {
18
18
  if (!(max > 0))
19
19
  return 0;
package/dist/frame.d.ts CHANGED
@@ -1,16 +1,28 @@
1
1
  import type { Store } from '@mosaicajs/client';
2
2
  /**
3
- * A box in world units, `[x0, y0, x1, y1]`, around where the items the store's filters admit are:
4
- * the smallest box of whole cells, at depth 8 over the view's extent, that holds every one of
5
- * them. An item far from the rest widens the box to take it in.
3
+ * Finds the box round the items that the store's filters match, so you can move the camera there
4
+ * with {@link cameraFitting}. The box is `[x0, y0, x1, y1]` in world units, made of whole cells 2
5
+ * units wide, or wider if the server's `selection.maxAggregateCells` is low. It holds every match,
6
+ * so one item far from the rest widens it.
6
7
  *
7
- * It asks one `POST /v1/aggregate` of the store, a count by cells over the store's filters and its
8
- * selected region, so the box is computed over the items this viewer may see. The request carries
9
- * the filters as they stand when it is sent, and is sent again if they change before the answer
10
- * lands. The registration is dropped once the answer lands.
8
+ * The count goes through the store, so the box covers only items this viewer may see, under the
9
+ * store's filters and selected region. If the filters change before the answer arrives, the store
10
+ * asks again. The promise waits for the store's `/v1/meta`.
11
11
  *
12
- * Resolves to `null` where no item matches, where the server refuses the count, where its
13
- * `selection.maxAggregateCells` admits no cell level, and once `signal` aborts. Waits for `meta`.
12
+ * @param store - The store whose filters are matched.
13
+ * @param options.signal - Aborts the search, which then resolves to `null`.
14
+ * @returns The box, or `null` if no item matches, if the server refuses or does not allow the
15
+ * count, or once `signal` aborts.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const box = await frameMatches(store);
20
+ * if (box) {
21
+ * viewState = {...viewState, ...cameraFitting(box, deck.width, deck.height)};
22
+ * deck.setProps({viewState});
23
+ * tell();
24
+ * }
25
+ * ```
14
26
  */
15
27
  export declare function frameMatches(store: Store, options?: {
16
28
  signal?: AbortSignal;
package/dist/frame.js CHANGED
@@ -8,17 +8,29 @@ import { densityCellsOf } from './density.js';
8
8
  const FRAME_DEPTH = 8;
9
9
  let framings = 0;
10
10
  /**
11
- * A box in world units, `[x0, y0, x1, y1]`, around where the items the store's filters admit are:
12
- * the smallest box of whole cells, at depth 8 over the view's extent, that holds every one of
13
- * them. An item far from the rest widens the box to take it in.
11
+ * Finds the box round the items that the store's filters match, so you can move the camera there
12
+ * with {@link cameraFitting}. The box is `[x0, y0, x1, y1]` in world units, made of whole cells 2
13
+ * units wide, or wider if the server's `selection.maxAggregateCells` is low. It holds every match,
14
+ * so one item far from the rest widens it.
14
15
  *
15
- * It asks one `POST /v1/aggregate` of the store, a count by cells over the store's filters and its
16
- * selected region, so the box is computed over the items this viewer may see. The request carries
17
- * the filters as they stand when it is sent, and is sent again if they change before the answer
18
- * lands. The registration is dropped once the answer lands.
16
+ * The count goes through the store, so the box covers only items this viewer may see, under the
17
+ * store's filters and selected region. If the filters change before the answer arrives, the store
18
+ * asks again. The promise waits for the store's `/v1/meta`.
19
19
  *
20
- * Resolves to `null` where no item matches, where the server refuses the count, where its
21
- * `selection.maxAggregateCells` admits no cell level, and once `signal` aborts. Waits for `meta`.
20
+ * @param store - The store whose filters are matched.
21
+ * @param options.signal - Aborts the search, which then resolves to `null`.
22
+ * @returns The box, or `null` if no item matches, if the server refuses or does not allow the
23
+ * count, or once `signal` aborts.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const box = await frameMatches(store);
28
+ * if (box) {
29
+ * viewState = {...viewState, ...cameraFitting(box, deck.width, deck.height)};
30
+ * deck.setProps({viewState});
31
+ * tell();
32
+ * }
33
+ * ```
22
34
  */
23
35
  export function frameMatches(store, options = {}) {
24
36
  framings += 1;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,55 @@
1
1
  /**
2
- * A deck.gl layer that draws a Mosaica store, for a host that builds its own `Deck`.
3
- * {@link MosaicaLayer} draws the marks, density, the artifacts' names and outlines and the
4
- * selection. {@link CATEGORY_PALETTES} and {@link RAMPS} are the named colour sets a
5
- * {@link Colouring} chooses from, and {@link Sizing} the sizes a number column sizes marks
6
- * between. {@link viewInputOf} turns the host's camera into a view for the store,
7
- * {@link DensityCounter} keeps the counts density is drawn from, {@link frameMatches} and
8
- * {@link cameraFitting} open the camera on the matched items, {@link resolvePick} reads a pick, and
9
- * {@link basemapLayers} draws a basemap under the points.
2
+ * `@mosaicajs/deck` draws a Mosaica map with deck.gl, for an app that builds its own `Deck`. If
3
+ * you want a ready-made map, the `<mosaica-map>` element in `@mosaicajs/components` wraps this
4
+ * package and handles the camera for you.
5
+ *
6
+ * {@link MosaicaLayer} is a deck.gl layer that draws what a store from `@mosaicajs/client`
7
+ * publishes: the points, density, the artifacts' names and outlines, and the selected region. It
8
+ * fetches nothing itself. Your app owns the camera. Each time the view state changes, pass the
9
+ * camera to {@link viewInputOf} and give the result to the store's `setView`. The store fetches
10
+ * what the new view needs, and the layer redraws when the store's projections change.
11
+ *
12
+ * The rest of the package works alongside the layer. {@link resolvePick} turns a deck.gl pick into
13
+ * the item or artifact under the pointer. {@link frameMatches} and {@link cameraFitting} find the
14
+ * camera that shows the filtered items. {@link basemapLayers} draws a world outline, map tiles or
15
+ * an image under the points. {@link DensityCounter} fetches the counts that density is drawn from,
16
+ * and the layer keeps one of its own unless you pass yours. {@link Colouring}, {@link Sizing} and
17
+ * the named palettes and ramps are the choices for colouring and sizing points by a column.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import {Deck, OrthographicView} from '@deck.gl/core';
22
+ * import {createStore} from '@mosaicajs/client';
23
+ * import {MosaicaLayer, viewInputOf} from '@mosaicajs/deck';
24
+ *
25
+ * type Camera = {target: [number, number, number]; zoom: number};
26
+ *
27
+ * const store = createStore({viewerUrl: 'http://localhost:8080', token});
28
+ * let viewState: Camera = {target: [256, 256, 0], zoom: 0};
29
+ *
30
+ * // Tell the store where the camera is. viewInputOf returns null until /v1/meta has arrived.
31
+ * function tell() {
32
+ * const input = viewInputOf(store, viewState, deck.width, deck.height);
33
+ * if (input) store.setView(input);
34
+ * }
35
+ *
36
+ * const deck = new Deck<OrthographicView>({
37
+ * parent: document.getElementById('map') as HTMLDivElement,
38
+ * views: new OrthographicView({flipY: true}),
39
+ * viewState,
40
+ * controller: true,
41
+ * layers: [new MosaicaLayer({id: 'mosaica', store})],
42
+ * onViewStateChange: ({viewState: next}) => {
43
+ * viewState = next as Camera;
44
+ * deck.setProps({viewState});
45
+ * tell();
46
+ * }
47
+ * });
48
+ *
49
+ * store.subscribe('meta', (meta) => {
50
+ * if (meta) tell();
51
+ * });
52
+ * ```
10
53
  *
11
54
  * @module @mosaicajs/deck
12
55
  */
package/dist/index.js CHANGED
@@ -1,12 +1,55 @@
1
1
  /**
2
- * A deck.gl layer that draws a Mosaica store, for a host that builds its own `Deck`.
3
- * {@link MosaicaLayer} draws the marks, density, the artifacts' names and outlines and the
4
- * selection. {@link CATEGORY_PALETTES} and {@link RAMPS} are the named colour sets a
5
- * {@link Colouring} chooses from, and {@link Sizing} the sizes a number column sizes marks
6
- * between. {@link viewInputOf} turns the host's camera into a view for the store,
7
- * {@link DensityCounter} keeps the counts density is drawn from, {@link frameMatches} and
8
- * {@link cameraFitting} open the camera on the matched items, {@link resolvePick} reads a pick, and
9
- * {@link basemapLayers} draws a basemap under the points.
2
+ * `@mosaicajs/deck` draws a Mosaica map with deck.gl, for an app that builds its own `Deck`. If
3
+ * you want a ready-made map, the `<mosaica-map>` element in `@mosaicajs/components` wraps this
4
+ * package and handles the camera for you.
5
+ *
6
+ * {@link MosaicaLayer} is a deck.gl layer that draws what a store from `@mosaicajs/client`
7
+ * publishes: the points, density, the artifacts' names and outlines, and the selected region. It
8
+ * fetches nothing itself. Your app owns the camera. Each time the view state changes, pass the
9
+ * camera to {@link viewInputOf} and give the result to the store's `setView`. The store fetches
10
+ * what the new view needs, and the layer redraws when the store's projections change.
11
+ *
12
+ * The rest of the package works alongside the layer. {@link resolvePick} turns a deck.gl pick into
13
+ * the item or artifact under the pointer. {@link frameMatches} and {@link cameraFitting} find the
14
+ * camera that shows the filtered items. {@link basemapLayers} draws a world outline, map tiles or
15
+ * an image under the points. {@link DensityCounter} fetches the counts that density is drawn from,
16
+ * and the layer keeps one of its own unless you pass yours. {@link Colouring}, {@link Sizing} and
17
+ * the named palettes and ramps are the choices for colouring and sizing points by a column.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import {Deck, OrthographicView} from '@deck.gl/core';
22
+ * import {createStore} from '@mosaicajs/client';
23
+ * import {MosaicaLayer, viewInputOf} from '@mosaicajs/deck';
24
+ *
25
+ * type Camera = {target: [number, number, number]; zoom: number};
26
+ *
27
+ * const store = createStore({viewerUrl: 'http://localhost:8080', token});
28
+ * let viewState: Camera = {target: [256, 256, 0], zoom: 0};
29
+ *
30
+ * // Tell the store where the camera is. viewInputOf returns null until /v1/meta has arrived.
31
+ * function tell() {
32
+ * const input = viewInputOf(store, viewState, deck.width, deck.height);
33
+ * if (input) store.setView(input);
34
+ * }
35
+ *
36
+ * const deck = new Deck<OrthographicView>({
37
+ * parent: document.getElementById('map') as HTMLDivElement,
38
+ * views: new OrthographicView({flipY: true}),
39
+ * viewState,
40
+ * controller: true,
41
+ * layers: [new MosaicaLayer({id: 'mosaica', store})],
42
+ * onViewStateChange: ({viewState: next}) => {
43
+ * viewState = next as Camera;
44
+ * deck.setProps({viewState});
45
+ * tell();
46
+ * }
47
+ * });
48
+ *
49
+ * store.subscribe('meta', (meta) => {
50
+ * if (meta) tell();
51
+ * });
52
+ * ```
10
53
  *
11
54
  * @module @mosaicajs/deck
12
55
  */