• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

keplergl / kepler.gl / 12482165950

24 Dec 2024 01:12PM UTC coverage: 67.451% (-0.1%) from 67.586%
12482165950

push

github

web-flow
[chore] 3.1.0-alpha.2 release (#2855)

- bump version for release
- temporarily disable vector tile layer plumbing
- ts fixes required for npm-publish
- prepublish to prepublishOnly script

5810 of 10000 branches covered (58.1%)

Branch coverage included in aggregate %.

4 of 9 new or added lines in 7 files covered. (44.44%)

29 existing lines in 4 files now uncovered.

11954 of 16336 relevant lines covered (73.18%)

86.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/layers/src/vector-tile/common-tile/tile-dataset.ts
1
// SPDX-License-Identifier: MIT
2
// Copyright contributors to the kepler.gl project
3

4
import {Field as KeplerField} from '@kepler.gl/types';
5
import {quickInsertionSort} from '@kepler.gl/utils';
6

7
import IterableTileSet, {RowCountAccessor} from './iterable-tile-set';
8
import {pruneQuantiles} from '../utils/vector-tile-utils';
9

10
export type Datum = number | string | null;
11

12
type RowValueAccessor<T> = (
13
  field: KeplerField,
14
  row: T extends Iterable<infer V> ? V : never
15
) => Datum;
16
type RowValueAccessorFactory<T> = (
17
  field?: KeplerField,
18
  indexKey?: number | null
19
) => RowValueAccessor<T>;
20

21
type TileAccessors<T, I extends Iterable<any> = T extends Iterable<any> ? T : never> = {
22
  getTileId: (tile: T) => string;
23
  getIterable: (tile: T) => I;
24
  getRowCount: RowCountAccessor<I>;
25
  getRowValue: RowValueAccessorFactory<I>;
26
};
27

28
/**
29
 * Per-tile stats, for caching
30
 */
31
type TileFieldStats = {
32
  extent?: [number, number];
33
  sample?: Datum[];
34
  uniqueValues?: Set<Datum>;
35
};
36

37
/**
38
 * Stateful class offering dataset-style functions for the set of tiles.
39
 */
40
export default class TileDataset<T, I extends Iterable<any> = T extends Iterable<any> ? T : never> {
41
  private accessors: TileAccessors<T, I>;
42
  private tiles: readonly T[];
43
  private tileSet: IterableTileSet<I>;
UNCOV
44
  private tileIds: Set<string> = new Set();
×
45

46
  /** Cache for per-tile field stats: tileId -> fieldId -> stats */
UNCOV
47
  private tileStats: Map<string, Map<string, TileFieldStats>> = new Map();
×
48

49
  constructor(accessors: TileAccessors<T, I>, tiles?: readonly T[]) {
UNCOV
50
    this.accessors = accessors;
×
UNCOV
51
    this.tiles = [];
×
UNCOV
52
    this.tileSet = new IterableTileSet([], accessors.getRowCount);
×
UNCOV
53
    if (tiles) {
×
54
      this.updateTiles(tiles);
×
55
    }
56
  }
57

58
  /**
59
   * Invalidate the cached data
60
   */
61
  invalidateCache(): void {
62
    // TODO: implement later
63
  }
64

65
  /**
66
   * Update the set of tiles in the viewport
67
   */
68
  updateTiles(tiles: readonly T[]): void {
69
    const {getTileId, getIterable, getRowCount} = this.accessors;
×
70
    const tileIds = new Set<string>(tiles.map(getTileId));
×
71
    if (!areEqualSets(tileIds, this.tileIds)) {
×
72
      this.invalidateCache();
×
73
    }
74
    this.tiles = tiles;
×
75
    this.tileIds = tileIds;
×
76
    this.tileSet = new IterableTileSet(tiles.map(getIterable), getRowCount);
×
77
  }
78

79
  /**
80
   * Get the min/max domain of a field
81
   */
82
  getExtent(field: KeplerField): [number, number] {
83
    const {getRowValue, getIterable} = this.accessors;
×
84
    const accessor = getRowValue(field);
×
85
    let min = Infinity;
×
86
    let max = -Infinity;
×
87

88
    for (const tile of this.tiles) {
×
89
      // Check the cache
90
      let extent = this.getTileStat(tile, field, 'extent');
×
91
      if (!extent) {
×
92
        // Cache miss, calculate and cache
93
        extent = getTileExtent(getIterable(tile), field, accessor);
×
94
        this.setTileStat(tile, field, 'extent', extent);
×
95
      }
96
      if (extent) {
×
97
        if (extent[0] < min) min = extent[0];
×
98
        if (extent[1] > max) max = extent[1];
×
99
      }
100
    }
101
    return Number.isFinite(min) && Number.isFinite(max) ? [min, max] : [0, 0];
×
102
  }
103

104
  /**
105
   * Get a sample of field values to use in estimating quantiles
106
   */
107
  getQuantileSample(field: KeplerField, minRowCount = 1000): number[] {
×
108
    // TODO: There should be reasonable per-tile caching possible here
109
    const set = this.tileSet;
×
110
    const accessor = this.accessors.getRowValue(field);
×
111
    const sample: number[] = [];
×
112
    const sampleStep = Math.max(Math.floor(set.rowCount / minRowCount), 1);
×
113
    let i = 0;
×
114
    for (const row of set) {
×
115
      if (++i === sampleStep) {
×
116
        const val = accessor(field, row) as number | null;
×
117
        if (val !== null) sample.push(val);
×
118
        i = 0;
×
119
      }
120
    }
121
    quickInsertionSort(sample);
×
122
    pruneQuantiles(sample);
×
123
    return sample;
×
124
  }
125

126
  /**
127
   * Get a set of unique values for a field
128
   */
129
  getUniqueValues(field: KeplerField): Datum[] {
130
    const {getRowValue, getIterable} = this.accessors;
×
131
    const accessor = getRowValue(field);
×
132
    const uniques = new Set<Datum>();
×
133

134
    for (const tile of this.tiles) {
×
135
      // Check the cache
136
      let tileUniques = this.getTileStat(tile, field, 'uniqueValues');
×
137
      if (!tileUniques) {
×
138
        // Cache miss, calculate and cache
139
        tileUniques = getTileUniqueValues(getIterable(tile), field, accessor);
×
140
        this.setTileStat(tile, field, 'uniqueValues', tileUniques);
×
141
      }
142
      for (const val of tileUniques ?? []) {
×
143
        uniques.add(val);
×
144
      }
145
    }
146
    return [...uniques];
×
147
  }
148

149
  private getTileStat<K extends keyof TileFieldStats>(
150
    tile: T,
151
    field: KeplerField,
152
    stat: K
153
  ): TileFieldStats[K] {
154
    return this.tileStats.get(this.accessors.getTileId(tile))?.get(field.name)?.[stat];
×
155
  }
156

157
  private setTileStat<K extends keyof TileFieldStats>(
158
    tile: T,
159
    field: KeplerField,
160
    stat: K,
161
    value: TileFieldStats[K]
162
  ): void {
163
    const tileId = this.accessors.getTileId(tile);
×
164
    const tileStats = this.tileStats.get(tileId) ?? new Map();
×
165
    const tileFieldStats = tileStats.get(field.name) ?? {};
×
166
    tileFieldStats[stat] = value;
×
167
    tileStats.set(field.name, tileFieldStats);
×
168
    this.tileStats.set(tileId, tileStats);
×
169
  }
170
}
171

172
/**
173
 * Get the min/max domain of a field in a given tile
174
 */
175
function getTileExtent<I extends Iterable<any>>(
176
  iterable: I,
177
  field: KeplerField,
178
  accessor: RowValueAccessor<I>
179
): [number, number] | undefined {
180
  let min = Infinity;
×
181
  let max = -Infinity;
×
182
  for (const row of iterable) {
×
183
    const val = accessor(field, row) as number | null;
×
184
    if (val === null) continue;
×
185
    if (val < min) min = val;
×
186
    if (val > max) max = val;
×
187
  }
188
  return Number.isFinite(min) && Number.isFinite(max) ? [min, max] : undefined;
×
189
}
190

191
/**
192
 * Get unique values for a field in a given tile
193
 */
194
function getTileUniqueValues<I extends Iterable<any>>(
195
  iterable: I,
196
  field: KeplerField,
197
  accessor: RowValueAccessor<I>,
198
  maxUniques = 20
×
199
): Set<Datum> {
200
  const uniques = new Set<Datum>();
×
201
  for (const row of iterable) {
×
202
    if (uniques.size >= maxUniques) return uniques;
×
203
    uniques.add(accessor(field, row));
×
204
  }
205
  return uniques;
×
206
}
207

208
function areEqualSets(a: Set<string>, b: Set<string>): boolean {
209
  if (a.size !== b.size) return false;
×
210
  for (const val of a) {
×
211
    if (!b.has(val)) {
×
212
      return false;
×
213
    }
214
  }
215
  return true;
×
216
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc