@basemaps/lambda-tiler 7.6.0 → 7.9.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/CHANGELOG.md +31 -0
- package/build/__tests__/config.data.js +3 -3
- package/build/__tests__/config.data.js.map +1 -1
- package/build/__tests__/tile.style.json.test.js +0 -5
- package/build/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/__tests__/imagery.test.js +39 -1
- package/build/routes/__tests__/imagery.test.js.map +1 -1
- package/build/routes/__tests__/tile.style.json.test.js +24 -0
- package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/imagery.js +4 -0
- package/build/routes/imagery.js.map +1 -1
- package/build/routes/tile.style.json.d.ts +8 -2
- package/build/routes/tile.style.json.js +59 -27
- package/build/routes/tile.style.json.js.map +1 -1
- package/build/routes/tile.xyz.raster.js.map +1 -1
- package/build/util/__test__/cache.test.d.ts +1 -0
- package/build/util/__test__/cache.test.js +29 -0
- package/build/util/__test__/cache.test.js.map +1 -0
- package/build/util/source.cache.d.ts +2 -8
- package/build/util/source.cache.js +6 -24
- package/build/util/source.cache.js.map +1 -1
- package/build/util/swapping.lru.d.ts +1 -0
- package/build/util/swapping.lru.js +4 -0
- package/build/util/swapping.lru.js.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/config.data.ts +3 -3
- package/src/__tests__/tile.style.json.test.ts +0 -7
- package/src/routes/__tests__/imagery.test.ts +51 -1
- package/src/routes/__tests__/tile.style.json.test.ts +31 -0
- package/src/routes/imagery.ts +4 -0
- package/src/routes/tile.style.json.ts +63 -25
- package/src/routes/tile.xyz.raster.ts +0 -1
- package/src/util/__test__/cache.test.ts +36 -0
- package/src/util/source.cache.ts +10 -20
- package/src/util/swapping.lru.ts +5 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { fsa, FsMemory } from '@chunkd/fs';
|
|
4
|
+
import { SourceCache } from '../source.cache.js';
|
|
5
|
+
describe('CoSourceCache', () => {
|
|
6
|
+
it('should not exit if a promise rejection happens for tiff', async () => {
|
|
7
|
+
const cache = new SourceCache(5);
|
|
8
|
+
const mem = new FsMemory();
|
|
9
|
+
const tiffLoc = new URL('memory://foo/bar.tiff');
|
|
10
|
+
await mem.write(tiffLoc, Buffer.from('ABC123'));
|
|
11
|
+
fsa.register('memory://', mem);
|
|
12
|
+
let failCount = 0;
|
|
13
|
+
await cache.getCog(tiffLoc).catch(() => failCount++);
|
|
14
|
+
assert.equal(cache.cache.currentSize, 0);
|
|
15
|
+
assert.equal(failCount, 1);
|
|
16
|
+
});
|
|
17
|
+
it('should not exit if a promise rejection happens for tar', async () => {
|
|
18
|
+
const cache = new SourceCache(5);
|
|
19
|
+
const mem = new FsMemory();
|
|
20
|
+
const tiffLoc = new URL('memory://foo/bar.tar');
|
|
21
|
+
await mem.write(tiffLoc, Buffer.from('ABC123'));
|
|
22
|
+
fsa.register('memory://', mem);
|
|
23
|
+
let failCount = 0;
|
|
24
|
+
await cache.getCotar(tiffLoc).catch(() => failCount++);
|
|
25
|
+
assert.equal(cache.cache.currentSize, 0);
|
|
26
|
+
assert.equal(failCount, 1);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../../src/util/__test__/cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;QAEjC,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChD,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAE/B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;QAEjC,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,CAAC;QAChD,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChD,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAE/B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,MAAM,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -4,24 +4,18 @@ export type LruStrut = LruStrutCotar | LruStrutCog;
|
|
|
4
4
|
export interface LruStrutCotar {
|
|
5
5
|
type: 'cotar';
|
|
6
6
|
value: Promise<Cotar>;
|
|
7
|
-
|
|
7
|
+
size: number;
|
|
8
8
|
}
|
|
9
9
|
export interface LruStrutCog {
|
|
10
10
|
type: 'cog';
|
|
11
11
|
value: Promise<Tiff>;
|
|
12
|
-
_value?: Tiff;
|
|
13
|
-
}
|
|
14
|
-
declare class LruStrutObj<T extends LruStrut> {
|
|
15
|
-
ob: T;
|
|
16
|
-
constructor(ob: T);
|
|
17
12
|
size: number;
|
|
18
13
|
}
|
|
19
14
|
export declare class SourceCache {
|
|
20
|
-
cache: SwappingLru<
|
|
15
|
+
cache: SwappingLru<LruStrut>;
|
|
21
16
|
constructor(maxSize: number);
|
|
22
17
|
getCog(location: URL): Promise<Tiff>;
|
|
23
18
|
getCotar(location: URL): Promise<Cotar>;
|
|
24
19
|
}
|
|
25
20
|
/** Cache the last 5,000 tiff/tar files accessed, generally it is only <100KB of memory used per tiff file so approx 50MB of cache */
|
|
26
21
|
export declare const CoSources: SourceCache;
|
|
27
|
-
export {};
|
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
import { Cotar, fsa, Tiff } from '@basemaps/shared';
|
|
2
2
|
import { SwappingLru } from './swapping.lru.js';
|
|
3
|
-
class LruStrutObj {
|
|
4
|
-
constructor(ob) {
|
|
5
|
-
Object.defineProperty(this, "ob", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
writable: true,
|
|
9
|
-
value: void 0
|
|
10
|
-
});
|
|
11
|
-
Object.defineProperty(this, "size", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
configurable: true,
|
|
14
|
-
writable: true,
|
|
15
|
-
value: 1
|
|
16
|
-
});
|
|
17
|
-
this.ob = ob;
|
|
18
|
-
if (this.ob._value == null) {
|
|
19
|
-
void this.ob.value.then((c) => (this.ob._value = c));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
3
|
export class SourceCache {
|
|
24
4
|
constructor(maxSize) {
|
|
25
5
|
Object.defineProperty(this, "cache", {
|
|
@@ -31,25 +11,27 @@ export class SourceCache {
|
|
|
31
11
|
this.cache = new SwappingLru(maxSize);
|
|
32
12
|
}
|
|
33
13
|
getCog(location) {
|
|
34
|
-
const existing = this.cache.get(location.href)
|
|
14
|
+
const existing = this.cache.get(location.href);
|
|
35
15
|
if (existing != null) {
|
|
36
16
|
if (existing.type === 'cog')
|
|
37
17
|
return existing.value;
|
|
38
18
|
throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`);
|
|
39
19
|
}
|
|
40
20
|
const value = Tiff.create(fsa.source(location));
|
|
41
|
-
this.cache.set(location.href,
|
|
21
|
+
this.cache.set(location.href, { type: 'cog', value, size: 1 });
|
|
22
|
+
value.catch(() => this.cache.delete(location.href));
|
|
42
23
|
return value;
|
|
43
24
|
}
|
|
44
25
|
getCotar(location) {
|
|
45
|
-
const existing = this.cache.get(location.href)
|
|
26
|
+
const existing = this.cache.get(location.href);
|
|
46
27
|
if (existing != null) {
|
|
47
28
|
if (existing.type === 'cotar')
|
|
48
29
|
return existing.value;
|
|
49
30
|
throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`);
|
|
50
31
|
}
|
|
51
32
|
const value = Cotar.fromTar(fsa.source(location));
|
|
52
|
-
this.cache.set(location.href,
|
|
33
|
+
this.cache.set(location.href, { type: 'cotar', value, size: 1 });
|
|
34
|
+
value.catch(() => this.cache.delete(location.href));
|
|
53
35
|
return value;
|
|
54
36
|
}
|
|
55
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.cache.js","sourceRoot":"","sources":["../../src/util/source.cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAgBhD,MAAM,
|
|
1
|
+
{"version":3,"file":"source.cache.js","sourceRoot":"","sources":["../../src/util/source.cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAgBhD,MAAM,OAAO,WAAW;IAEtB,YAAY,OAAe;QAD3B;;;;;WAA6B;QAE3B,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAW,OAAO,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,CAAC,QAAa;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO,QAAQ,CAAC,KAAK,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,IAAI,uBAAuB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QACnG,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/D,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ,CAAC,QAAa;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,QAAQ,CAAC,KAAK,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,IAAI,uBAAuB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QACnG,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACjE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,qIAAqI;AACrI,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swapping.lru.js","sourceRoot":"","sources":["../../src/util/swapping.lru.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IAYtB,YAAY,OAAe;QAX3B;;;;mBAAyB,IAAI,GAAG,EAAE;WAAC;QACnC;;;;mBAAyB,IAAI,GAAG,EAAE;WAAC;QACnC;;;;;WAAgB;QAEhB;;;;mBAAO,CAAC;WAAC;QACT;;;;mBAAS,CAAC;WAAC;QACX;;;;mBAAS,CAAC;WAAC;QACX;;;;mBAAS,CAAC;WAAC;QAEX;;;;mBAAiB,CAAC,CAAC;WAAC;QAGlB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,0DAA0D;QAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,sBAAsB;IACtB,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,GAAG,CAAC,EAAU,EAAE,IAAO;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,sDAAsD;IACtD,KAAK;QACH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC;YAAE,OAAO;QAC9B,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,qFAAqF;QACrF,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAChB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;IAC1B,CAAC;IAED,6DAA6D;IAC7D,IAAI,WAAW;QACb,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"swapping.lru.js","sourceRoot":"","sources":["../../src/util/swapping.lru.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IAYtB,YAAY,OAAe;QAX3B;;;;mBAAyB,IAAI,GAAG,EAAE;WAAC;QACnC;;;;mBAAyB,IAAI,GAAG,EAAE;WAAC;QACnC;;;;;WAAgB;QAEhB;;;;mBAAO,CAAC;WAAC;QACT;;;;mBAAS,CAAC;WAAC;QACX;;;;mBAAS,CAAC;WAAC;QACX;;;;mBAAS,CAAC;WAAC;QAEX;;;;mBAAiB,CAAC,CAAC;WAAC;QAGlB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,0DAA0D;QAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,sBAAsB;IACtB,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,EAAU,EAAE,IAAO;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,sDAAsD;IACtD,KAAK;QACH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC;YAAE,OAAO;QAC9B,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,qFAAqF;QACrF,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAChB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;IAC1B,CAAC;IAED,6DAA6D;IAC7D,IAAI,WAAW;QACb,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basemaps/lambda-tiler",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.9.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/linz/basemaps.git",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"types": "./build/index.d.ts",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@basemaps/config": "^7.
|
|
26
|
-
"@basemaps/config-loader": "^7.
|
|
25
|
+
"@basemaps/config": "^7.7.0",
|
|
26
|
+
"@basemaps/config-loader": "^7.9.0",
|
|
27
27
|
"@basemaps/geo": "^7.5.0",
|
|
28
|
-
"@basemaps/shared": "^7.
|
|
28
|
+
"@basemaps/shared": "^7.9.0",
|
|
29
29
|
"@basemaps/tiler": "^7.5.0",
|
|
30
|
-
"@basemaps/tiler-sharp": "^7.
|
|
30
|
+
"@basemaps/tiler-sharp": "^7.7.0",
|
|
31
31
|
"@linzjs/geojson": "^7.5.0",
|
|
32
32
|
"@linzjs/lambda": "^4.0.0",
|
|
33
33
|
"p-limit": "^4.0.0",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"@types/pixelmatch": "^5.0.0",
|
|
56
56
|
"pretty-json-log": "^1.0.0"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "5dc4332d74563f6ca6ac644258728eb64ceacf6a"
|
|
59
59
|
}
|
|
@@ -33,9 +33,9 @@ export const TileSetAerial: ConfigTileSetRaster = {
|
|
|
33
33
|
export const TileSetVector: ConfigTileSetVector = {
|
|
34
34
|
id: 'ts_topographic',
|
|
35
35
|
type: TileSetType.Vector,
|
|
36
|
-
name: '
|
|
37
|
-
description: '
|
|
38
|
-
title: '
|
|
36
|
+
name: 'topographic',
|
|
37
|
+
description: 'topographic__description',
|
|
38
|
+
title: 'topographic Imagery',
|
|
39
39
|
category: 'Basemap',
|
|
40
40
|
layers: [
|
|
41
41
|
{
|
|
@@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
|
4
4
|
import { StyleJson } from '@basemaps/config';
|
|
5
5
|
import { GoogleTms, Nztm2000QuadTms } from '@basemaps/geo';
|
|
6
6
|
import { Env } from '@basemaps/shared';
|
|
7
|
-
import { LambdaHttpResponse } from '@linzjs/lambda';
|
|
8
7
|
|
|
9
8
|
import { convertRelativeUrl, convertStyleJson } from '../routes/tile.style.json.js';
|
|
10
9
|
|
|
@@ -153,12 +152,6 @@ describe('TileStyleJson', () => {
|
|
|
153
152
|
});
|
|
154
153
|
});
|
|
155
154
|
|
|
156
|
-
it('should thrown error for NZTM2000Quad with vector source', () => {
|
|
157
|
-
const converted = (): StyleJson => convertStyleJson(baseStyleJson, Nztm2000QuadTms, 'abc123', null);
|
|
158
|
-
|
|
159
|
-
assert.throws(converted, LambdaHttpResponse);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
155
|
it('should convert relative glyphs and sprites', () => {
|
|
163
156
|
const apiKey = '0x9f9f';
|
|
164
157
|
const converted = convertStyleJson(baseStyleJson, GoogleTms, apiKey, null);
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
2
|
+
import { before, beforeEach, describe, it } from 'node:test';
|
|
3
3
|
|
|
4
|
+
import { ConfigProviderMemory } from '@basemaps/config';
|
|
5
|
+
import { fsa, FsMemory } from '@chunkd/fs';
|
|
6
|
+
|
|
7
|
+
import { Imagery3857 } from '../../__tests__/config.data.js';
|
|
8
|
+
import { mockRequest } from '../../__tests__/xyz.util.js';
|
|
9
|
+
import { handler } from '../../index.js';
|
|
10
|
+
import { ConfigLoader } from '../../util/config.loader.js';
|
|
4
11
|
import { isAllowedFile } from '../imagery.js';
|
|
5
12
|
|
|
6
13
|
describe('ImageryRoute', () => {
|
|
14
|
+
const memory = new FsMemory();
|
|
15
|
+
const config = new ConfigProviderMemory();
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
fsa.register('memory://imagery/', memory);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
config.assets = 'fake-s3://assets/';
|
|
23
|
+
});
|
|
24
|
+
|
|
7
25
|
it('should allow geojson and json files only', () => {
|
|
8
26
|
assert.equal(isAllowedFile('foo.geojson'), true);
|
|
9
27
|
assert.equal(isAllowedFile('foo.json'), true);
|
|
@@ -12,4 +30,36 @@ describe('ImageryRoute', () => {
|
|
|
12
30
|
assert.equal(isAllowedFile(''), false);
|
|
13
31
|
assert.equal(isAllowedFile(null as unknown as string), false);
|
|
14
32
|
});
|
|
33
|
+
|
|
34
|
+
it('should force download geojson files', async (t) => {
|
|
35
|
+
const fakeImagery = { ...Imagery3857 };
|
|
36
|
+
config.put(fakeImagery);
|
|
37
|
+
fakeImagery.uri = 'memory://imagery/';
|
|
38
|
+
|
|
39
|
+
t.mock.method(ConfigLoader, 'getDefaultConfig', () => Promise.resolve(config));
|
|
40
|
+
const res404 = await handler.router.handle(mockRequest(`/v1/imagery/${fakeImagery.id}/capture-area.geojson`));
|
|
41
|
+
assert.equal(res404.status, 404);
|
|
42
|
+
|
|
43
|
+
await memory.write(fsa.toUrl('memory://imagery/capture-area.geojson'), JSON.stringify({ hello: 'world' }));
|
|
44
|
+
|
|
45
|
+
const res200 = await handler.router.handle(mockRequest(`/v1/imagery/${fakeImagery.id}/capture-area.geojson`));
|
|
46
|
+
assert.equal(res200.status, 200);
|
|
47
|
+
assert.equal(res200.headers.get('content-disposition'), 'attachment');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should fetch stac files', async (t) => {
|
|
51
|
+
const fakeImagery = { ...Imagery3857 };
|
|
52
|
+
config.put(fakeImagery);
|
|
53
|
+
fakeImagery.uri = 'memory://imagery/';
|
|
54
|
+
|
|
55
|
+
t.mock.method(ConfigLoader, 'getDefaultConfig', () => Promise.resolve(config));
|
|
56
|
+
const res404 = await handler.router.handle(mockRequest(`/v1/imagery/${fakeImagery.id}/collection.json`));
|
|
57
|
+
assert.equal(res404.status, 404);
|
|
58
|
+
|
|
59
|
+
await memory.write(fsa.toUrl('memory://imagery/collection.json'), JSON.stringify({ hello: 'world' }));
|
|
60
|
+
|
|
61
|
+
const res200 = await handler.router.handle(mockRequest(`/v1/imagery/${fakeImagery.id}/collection.json`));
|
|
62
|
+
assert.equal(res200.status, 200);
|
|
63
|
+
assert.equal(res200.headers.get('content-disposition'), undefined);
|
|
64
|
+
});
|
|
15
65
|
});
|
|
@@ -380,4 +380,35 @@ describe('/v1/styles', () => {
|
|
|
380
380
|
assert.deepEqual(terrain.source, 'LINZ-Terrain');
|
|
381
381
|
assert.deepEqual(terrain.exaggeration, 1.2);
|
|
382
382
|
});
|
|
383
|
+
|
|
384
|
+
it('should set labels via parameter', async () => {
|
|
385
|
+
config.put(TileSetAerial);
|
|
386
|
+
config.put(TileSetElevation);
|
|
387
|
+
|
|
388
|
+
const fakeStyle = { id: 'st_labels', name: 'labels', style: fakeVectorStyleConfig };
|
|
389
|
+
config.put(fakeStyle);
|
|
390
|
+
|
|
391
|
+
const request = mockUrlRequest('/v1/styles/aerial.json', `?terrain=LINZ-Terrain&labels=true`, Api.header);
|
|
392
|
+
const res = await handler.router.handle(request);
|
|
393
|
+
assert.equal(res.status, 200, res.statusDescription);
|
|
394
|
+
|
|
395
|
+
const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
|
|
396
|
+
const terrain = body.terrain as unknown as Terrain;
|
|
397
|
+
assert.deepEqual(terrain.source, 'LINZ-Terrain');
|
|
398
|
+
assert.deepEqual(terrain.exaggeration, 1.2);
|
|
399
|
+
|
|
400
|
+
assert.equal(body.sources['basemaps_vector']?.type, 'vector');
|
|
401
|
+
assert.equal(body.layers.length, 2);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should error when joining layers with duplicate ids', async () => {
|
|
405
|
+
const fakeStyle = { id: 'st_labels', name: 'labels', style: fakeVectorStyleConfig };
|
|
406
|
+
config.put(fakeStyle);
|
|
407
|
+
config.put(TileSetAerial);
|
|
408
|
+
|
|
409
|
+
const request = mockUrlRequest('/v1/styles/labels.json', `?labels=true`, Api.header);
|
|
410
|
+
const res = await handler.router.handle(request);
|
|
411
|
+
assert.equal(res.status, 400, res.statusDescription);
|
|
412
|
+
assert.equal(res.statusDescription.includes('Background1'), true);
|
|
413
|
+
});
|
|
383
414
|
});
|
package/src/routes/imagery.ts
CHANGED
|
@@ -51,6 +51,10 @@ export async function imageryGet(req: LambdaHttpRequest<ImageryGet>): Promise<La
|
|
|
51
51
|
response.header(HttpHeader.ETag, cacheKey);
|
|
52
52
|
response.header(HttpHeader.ContentEncoding, 'gzip');
|
|
53
53
|
response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
|
|
54
|
+
// Force geojson files to be downloaded
|
|
55
|
+
if (requestedFile.endsWith('.geojson')) {
|
|
56
|
+
response.header('Content-Disposition', 'attachment');
|
|
57
|
+
}
|
|
54
58
|
response.buffer(isGzip(buf) ? buf : await gzipP(buf), 'application/json');
|
|
55
59
|
req.set('bytes', buf.byteLength);
|
|
56
60
|
return response;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConfigId, ConfigPrefix, ConfigTileSetRaster, Layer, Sources, StyleJson, TileSetType } from '@basemaps/config';
|
|
2
|
+
import { DefaultExaggeration } from '@basemaps/config/build/config/vector.style.js';
|
|
2
3
|
import { GoogleTms, TileMatrixSet, TileMatrixSets } from '@basemaps/geo';
|
|
3
4
|
import { Env, toQueryString } from '@basemaps/shared';
|
|
4
5
|
import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
|
|
@@ -47,9 +48,6 @@ export function convertStyleJson(
|
|
|
47
48
|
const sources = JSON.parse(JSON.stringify(style.sources)) as Sources;
|
|
48
49
|
for (const [key, value] of Object.entries(sources)) {
|
|
49
50
|
if (value.type === 'vector') {
|
|
50
|
-
if (tileMatrix !== GoogleTms) {
|
|
51
|
-
throw new LambdaHttpResponse(400, `TileMatrix is not supported for the vector source ${value.url}.`);
|
|
52
|
-
}
|
|
53
51
|
value.url = convertRelativeUrl(value.url, tileMatrix, apiKey, config);
|
|
54
52
|
} else if ((value.type === 'raster' || value.type === 'raster-dem') && Array.isArray(value.tiles)) {
|
|
55
53
|
for (let i = 0; i < value.tiles.length; i++) {
|
|
@@ -71,6 +69,7 @@ export function convertStyleJson(
|
|
|
71
69
|
if (style.glyphs) styleJson.glyphs = convertRelativeUrl(style.glyphs, undefined, undefined, config);
|
|
72
70
|
if (style.sprite) styleJson.sprite = convertRelativeUrl(style.sprite, undefined, undefined, config);
|
|
73
71
|
if (style.sky) styleJson.sky = style.sky;
|
|
72
|
+
if (style.terrain) styleJson.terrain = style.terrain;
|
|
74
73
|
|
|
75
74
|
return styleJson;
|
|
76
75
|
}
|
|
@@ -81,15 +80,48 @@ export interface StyleGet {
|
|
|
81
80
|
};
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
export interface StyleConfig {
|
|
84
|
+
/** Name of the terrain layer */
|
|
85
|
+
terrain?: string | null;
|
|
86
|
+
/** Combine layer with the labels layer */
|
|
87
|
+
labels: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setStyleTerrain(style: StyleJson, terrain: string, tileMatrix: TileMatrixSet): void {
|
|
85
91
|
const source = Object.keys(style.sources).find((s) => s === terrain);
|
|
86
92
|
if (source == null) throw new LambdaHttpResponse(400, `Terrain: ${terrain} is not exists in the style source.`);
|
|
87
93
|
style.terrain = {
|
|
88
94
|
source,
|
|
89
|
-
exaggeration:
|
|
95
|
+
exaggeration: DefaultExaggeration[tileMatrix.identifier] ?? DefaultExaggeration[GoogleTms.identifier],
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
|
|
99
|
+
async function setStyleLabels(req: LambdaHttpRequest<StyleGet>, style: StyleJson): Promise<void> {
|
|
100
|
+
const config = await ConfigLoader.load(req);
|
|
101
|
+
const labels = await config.Style.get('labels');
|
|
102
|
+
|
|
103
|
+
if (labels == null) {
|
|
104
|
+
req.log.warn('LabelsStyle:Missing');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const layerId = new Set<string>();
|
|
109
|
+
for (const l of style.layers) layerId.add(l.id);
|
|
110
|
+
|
|
111
|
+
for (const newLayers of labels.style.layers) {
|
|
112
|
+
if (layerId.has(newLayers.id)) {
|
|
113
|
+
throw new LambdaHttpResponse(400, 'Cannot merge styles with duplicate layerIds: ' + newLayers.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (style.glyphs == null) style.glyphs = labels.style.glyphs;
|
|
118
|
+
if (style.sprite == null) style.sprite = labels.style.sprite;
|
|
119
|
+
if (style.sky == null) style.sky = labels.style.sky;
|
|
120
|
+
|
|
121
|
+
Object.assign(style.sources, labels.style.sources);
|
|
122
|
+
style.layers = style.layers.concat(labels.style.layers);
|
|
123
|
+
}
|
|
124
|
+
|
|
93
125
|
async function ensureTerrain(
|
|
94
126
|
req: LambdaHttpRequest<StyleGet>,
|
|
95
127
|
tileMatrix: TileMatrixSet,
|
|
@@ -97,17 +129,16 @@ async function ensureTerrain(
|
|
|
97
129
|
style: StyleJson,
|
|
98
130
|
): Promise<void> {
|
|
99
131
|
const config = await ConfigLoader.load(req);
|
|
100
|
-
const terrain = await config.TileSet.get('
|
|
101
|
-
if (terrain)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
132
|
+
const terrain = await config.TileSet.get('elevation');
|
|
133
|
+
if (terrain == null) return;
|
|
134
|
+
const configLocation = ConfigLoader.extract(req);
|
|
135
|
+
const elevationQuery = toQueryString({ config: configLocation, api: apiKey, pipeline: 'terrain-rgb' });
|
|
136
|
+
style.sources['LINZ-Terrain'] = {
|
|
137
|
+
type: 'raster-dem',
|
|
138
|
+
tileSize: 256,
|
|
139
|
+
maxzoom: 18,
|
|
140
|
+
tiles: [convertRelativeUrl(`/v1/tiles/elevation/${tileMatrix.identifier}/{z}/{x}/{y}.png${elevationQuery}`)],
|
|
141
|
+
};
|
|
111
142
|
}
|
|
112
143
|
|
|
113
144
|
export async function tileSetToStyle(
|
|
@@ -115,7 +146,7 @@ export async function tileSetToStyle(
|
|
|
115
146
|
tileSet: ConfigTileSetRaster,
|
|
116
147
|
tileMatrix: TileMatrixSet,
|
|
117
148
|
apiKey: string,
|
|
118
|
-
|
|
149
|
+
cfg: StyleConfig,
|
|
119
150
|
): Promise<LambdaHttpResponse> {
|
|
120
151
|
const [tileFormat] = Validate.getRequestedFormats(req) ?? ['webp'];
|
|
121
152
|
if (tileFormat == null) return new LambdaHttpResponse(400, 'Invalid image format');
|
|
@@ -143,9 +174,10 @@ export async function tileSetToStyle(
|
|
|
143
174
|
await ensureTerrain(req, tileMatrix, apiKey, style);
|
|
144
175
|
|
|
145
176
|
// Add terrain in style
|
|
146
|
-
if (terrain) setStyleTerrain(style, terrain);
|
|
177
|
+
if (cfg.terrain) setStyleTerrain(style, cfg.terrain, tileMatrix);
|
|
178
|
+
if (cfg.labels) await setStyleLabels(req, style);
|
|
147
179
|
|
|
148
|
-
const data = Buffer.from(JSON.stringify(style));
|
|
180
|
+
const data = Buffer.from(JSON.stringify(convertStyleJson(style, tileMatrix, apiKey, configLocation)));
|
|
149
181
|
|
|
150
182
|
const cacheKey = Etag.key(data);
|
|
151
183
|
if (Etag.isNotModified(req, cacheKey)) return NotModified();
|
|
@@ -163,7 +195,7 @@ export async function tileSetOutputToStyle(
|
|
|
163
195
|
tileSet: ConfigTileSetRaster,
|
|
164
196
|
tileMatrix: TileMatrixSet,
|
|
165
197
|
apiKey: string,
|
|
166
|
-
|
|
198
|
+
cfg: StyleConfig,
|
|
167
199
|
): Promise<LambdaHttpResponse> {
|
|
168
200
|
const configLocation = ConfigLoader.extract(req);
|
|
169
201
|
const query = toQueryString({ config: configLocation, api: apiKey });
|
|
@@ -226,9 +258,10 @@ export async function tileSetOutputToStyle(
|
|
|
226
258
|
await ensureTerrain(req, tileMatrix, apiKey, style);
|
|
227
259
|
|
|
228
260
|
// Add terrain in style
|
|
229
|
-
if (terrain) setStyleTerrain(style, terrain);
|
|
261
|
+
if (cfg.terrain) setStyleTerrain(style, cfg.terrain, tileMatrix);
|
|
262
|
+
if (cfg.labels) await setStyleLabels(req, style);
|
|
230
263
|
|
|
231
|
-
const data = Buffer.from(JSON.stringify(style));
|
|
264
|
+
const data = Buffer.from(JSON.stringify(convertStyleJson(style, tileMatrix, apiKey, configLocation)));
|
|
232
265
|
|
|
233
266
|
const cacheKey = Etag.key(data);
|
|
234
267
|
if (Etag.isNotModified(req, cacheKey)) return Promise.resolve(NotModified());
|
|
@@ -249,18 +282,22 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
|
|
|
249
282
|
const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
|
|
250
283
|
if (tileMatrix == null) return new LambdaHttpResponse(400, 'Invalid tile matrix');
|
|
251
284
|
const terrain = req.query.get('terrain') ?? undefined;
|
|
285
|
+
const labels = Boolean(req.query.get('labels') ?? false);
|
|
252
286
|
|
|
253
287
|
// Get style Config from db
|
|
254
288
|
const config = await ConfigLoader.load(req);
|
|
255
289
|
const dbId = config.Style.id(styleName);
|
|
256
290
|
const styleConfig = await config.Style.get(dbId);
|
|
291
|
+
|
|
292
|
+
req.set('styleConfig', { terrain, labels });
|
|
293
|
+
|
|
257
294
|
if (styleConfig == null) {
|
|
258
295
|
// Were we given a tileset name instead, generated
|
|
259
296
|
const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
|
|
260
297
|
if (tileSet == null) return NotFound();
|
|
261
298
|
if (tileSet.type !== TileSetType.Raster) return NotFound();
|
|
262
|
-
if (tileSet.outputs) return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, terrain);
|
|
263
|
-
else return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, terrain);
|
|
299
|
+
if (tileSet.outputs) return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, { terrain, labels });
|
|
300
|
+
else return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, { terrain, labels });
|
|
264
301
|
}
|
|
265
302
|
|
|
266
303
|
// Prepare sources and add linz source
|
|
@@ -277,7 +314,8 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
|
|
|
277
314
|
await ensureTerrain(req, tileMatrix, apiKey, style);
|
|
278
315
|
|
|
279
316
|
// Add terrain in style
|
|
280
|
-
if (terrain) setStyleTerrain(style, terrain);
|
|
317
|
+
if (terrain) setStyleTerrain(style, terrain, tileMatrix);
|
|
318
|
+
if (labels) await setStyleLabels(req, style);
|
|
281
319
|
|
|
282
320
|
const data = Buffer.from(JSON.stringify(style));
|
|
283
321
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { fsa, FsMemory } from '@chunkd/fs';
|
|
5
|
+
|
|
6
|
+
import { SourceCache } from '../source.cache.js';
|
|
7
|
+
|
|
8
|
+
describe('CoSourceCache', () => {
|
|
9
|
+
it('should not exit if a promise rejection happens for tiff', async () => {
|
|
10
|
+
const cache = new SourceCache(5);
|
|
11
|
+
|
|
12
|
+
const mem = new FsMemory();
|
|
13
|
+
const tiffLoc = new URL('memory://foo/bar.tiff');
|
|
14
|
+
await mem.write(tiffLoc, Buffer.from('ABC123'));
|
|
15
|
+
fsa.register('memory://', mem);
|
|
16
|
+
|
|
17
|
+
let failCount = 0;
|
|
18
|
+
await cache.getCog(tiffLoc).catch(() => failCount++);
|
|
19
|
+
assert.equal(cache.cache.currentSize, 0);
|
|
20
|
+
assert.equal(failCount, 1);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should not exit if a promise rejection happens for tar', async () => {
|
|
24
|
+
const cache = new SourceCache(5);
|
|
25
|
+
|
|
26
|
+
const mem = new FsMemory();
|
|
27
|
+
const tiffLoc = new URL('memory://foo/bar.tar');
|
|
28
|
+
await mem.write(tiffLoc, Buffer.from('ABC123'));
|
|
29
|
+
fsa.register('memory://', mem);
|
|
30
|
+
|
|
31
|
+
let failCount = 0;
|
|
32
|
+
await cache.getCotar(tiffLoc).catch(() => failCount++);
|
|
33
|
+
assert.equal(cache.cache.currentSize, 0);
|
|
34
|
+
assert.equal(failCount, 1);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/src/util/source.cache.ts
CHANGED
|
@@ -7,54 +7,44 @@ export type LruStrut = LruStrutCotar | LruStrutCog;
|
|
|
7
7
|
export interface LruStrutCotar {
|
|
8
8
|
type: 'cotar';
|
|
9
9
|
value: Promise<Cotar>;
|
|
10
|
-
|
|
10
|
+
size: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface LruStrutCog {
|
|
14
14
|
type: 'cog';
|
|
15
15
|
value: Promise<Tiff>;
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
class LruStrutObj<T extends LruStrut> {
|
|
20
|
-
ob: T;
|
|
21
|
-
constructor(ob: T) {
|
|
22
|
-
this.ob = ob;
|
|
23
|
-
if (this.ob._value == null) {
|
|
24
|
-
void this.ob.value.then((c) => (this.ob._value = c));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
size = 1;
|
|
16
|
+
size: number;
|
|
29
17
|
}
|
|
30
18
|
|
|
31
19
|
export class SourceCache {
|
|
32
|
-
cache: SwappingLru<
|
|
20
|
+
cache: SwappingLru<LruStrut>;
|
|
33
21
|
constructor(maxSize: number) {
|
|
34
|
-
this.cache = new SwappingLru<
|
|
22
|
+
this.cache = new SwappingLru<LruStrut>(maxSize);
|
|
35
23
|
}
|
|
36
24
|
|
|
37
25
|
getCog(location: URL): Promise<Tiff> {
|
|
38
|
-
const existing = this.cache.get(location.href)
|
|
26
|
+
const existing = this.cache.get(location.href);
|
|
39
27
|
|
|
40
28
|
if (existing != null) {
|
|
41
29
|
if (existing.type === 'cog') return existing.value;
|
|
42
30
|
throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`);
|
|
43
31
|
}
|
|
44
32
|
const value = Tiff.create(fsa.source(location));
|
|
45
|
-
this.cache.set(location.href,
|
|
33
|
+
this.cache.set(location.href, { type: 'cog', value, size: 1 });
|
|
34
|
+
value.catch(() => this.cache.delete(location.href));
|
|
46
35
|
return value;
|
|
47
36
|
}
|
|
48
37
|
|
|
49
38
|
getCotar(location: URL): Promise<Cotar> {
|
|
50
|
-
const existing = this.cache.get(location.href)
|
|
39
|
+
const existing = this.cache.get(location.href);
|
|
51
40
|
|
|
52
41
|
if (existing != null) {
|
|
53
42
|
if (existing.type === 'cotar') return existing.value;
|
|
54
43
|
throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`);
|
|
55
44
|
}
|
|
56
45
|
const value = Cotar.fromTar(fsa.source(location));
|
|
57
|
-
this.cache.set(location.href,
|
|
46
|
+
this.cache.set(location.href, { type: 'cotar', value, size: 1 });
|
|
47
|
+
value.catch(() => this.cache.delete(location.href));
|
|
58
48
|
return value;
|
|
59
49
|
}
|
|
60
50
|
}
|
package/src/util/swapping.lru.ts
CHANGED
|
@@ -41,6 +41,11 @@ export class SwappingLru<T extends { size: number }> {
|
|
|
41
41
|
this.clears++;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
delete(id: string): void {
|
|
45
|
+
this.cacheA.delete(id);
|
|
46
|
+
this.cacheB.delete(id);
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
set(id: string, tiff: T): void {
|
|
45
50
|
this.cacheA.set(id, tiff);
|
|
46
51
|
this.check();
|