Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/geo/src/proj/__tests__/projection.loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import assert from 'node:assert';
import { describe, it, TestContext } from 'node:test';

import { PROJJSONDefinition } from 'proj4/dist/lib/core.js';

import { Aitm2000Json } from '../json/nzoi/aitm2000.js';
import { ProjectionLoader } from '../projection.loader.js';

/** Arbitrary EPSG code not in ProjJsons and not pre-registered */
const TestCode: number = 99999;

describe('ProjectionLoader', () => {
it('should handle concurrent loads for the same code without throwing', async (t: TestContext) => {
t.mock.method(
ProjectionLoader,
'fetchProjJson',
(): Promise<PROJJSONDefinition> =>
Promise.resolve({ ...Aitm2000Json, id: { authority: 'EPSG', code: TestCode } }),
);

const [epsgA, epsgB] = await Promise.all([ProjectionLoader.load(TestCode), ProjectionLoader.load(TestCode)]);

assert.equal(epsgA.code, TestCode);
assert.equal(epsgB.code, TestCode);
assert.equal(epsgA, epsgB);
});
});
15 changes: 13 additions & 2 deletions packages/geo/src/proj/projection.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,27 @@ export class ProjectionLoader {
// Exposed for testing
static _fetch = fetch;

private static _inflight = new Map<number, Promise<Epsg>>();

/**
* Initialises an Epsg instance for the given code and ensures that
* a corresponding Projection instance is available.
*
* @param code - The code for which to initialise an Epsg and Projection instance.
* @returns an Epsg instance
*/
static async load(code: number): Promise<Epsg> {
if (Projection.tryGet(code) != null) return Epsg.get(code);
static load(code: number): Promise<Epsg> {
if (Projection.tryGet(code) != null) return Promise.resolve(Epsg.get(code));

let promise = this._inflight.get(code);
if (promise == null) {
promise = this._load(code).finally(() => this._inflight.delete(code));
this._inflight.set(code, promise);
}
return promise;
}

private static async _load(code: number): Promise<Epsg> {
let projJson: PROJJSONDefinition;

if (UtmZone.isUTMZoneCode(code)) {
Expand Down
Loading