Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function to retrieve and cache vector files #62

Merged
merged 2 commits into from
Apr 12, 2021
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
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@
}
},
"dependencies": {
"@types/geojson": "^7946.0.7",
"@types/lru-cache": "^5.1.0",
"@types/topojson-client": "^3.0.0",
"@types/topojson-specification": "^1.0.1",
"lodash": "^4.17.15",
"semver": "7.3.2"
"lru-cache": "^6.0.0",
"semver": "7.3.2",
"topojson-client": "^3.1.0"
},
"devDependencies": {
"@babel/cli": "^7.6.4",
Expand Down
23 changes: 20 additions & 3 deletions src/ems_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import _ from 'lodash';
import { TMSService } from './tms_service';
import { EMSFormatType, FileLayer } from './file_layer';
import { FeatureCollection } from 'geojson';
import semver from 'semver';
import { format as formatUrl, parse as parseUrl, UrlObject } from 'url';
import { toAbsoluteUrl } from './utils';
import { ParsedUrlQueryInput } from 'querystring';
import LRUCache from 'lru-cache';

const DEFAULT_EMS_VERSION = '7.12';

Expand Down Expand Up @@ -110,6 +112,7 @@ type BaseClientConfig = {
landingPageUrl?: string;
fetchFunction: Function;
proxyPath?: string;
cacheSize?: number;
};

type DeprecatedClientConfig = BaseClientConfig & {
Expand Down Expand Up @@ -216,6 +219,7 @@ export class EMSClient {
private readonly _emsLandingPageUrl: string;
private readonly _language: string;
private readonly _proxyPath: string;
private readonly _cache: LRUCache<string, FeatureCollection>;

/**
* these methods are assigned outside the constructor
Expand Down Expand Up @@ -253,6 +257,9 @@ export class EMSClient {

this._fetchFunction = config.fetchFunction;
this._proxyPath = config.proxyPath || '';
this._cache = new LRUCache<string, FeatureCollection>({
max: config.cacheSize || 10,
});

this._invalidateSettings();
}
Expand All @@ -275,9 +282,9 @@ export class EMSClient {
/**
* this internal method is overridden by the tests to simulate custom manifest.
*/
async getManifest<T>(manifestUrl: string): Promise<T> {
async getManifest<T>(endpointUrl: string): Promise<T> {
try {
const url = extendUrl(manifestUrl, { query: this._queryParams });
const url = extendUrl(endpointUrl, { query: this._queryParams });
const result = await this._fetchWithTimeout(url);
return result ? await result.json() : null;
} catch (e) {
Expand All @@ -287,7 +294,7 @@ export class EMSClient {
if (!(e instanceof Error)) {
e = new Error(e.data || `status ${e.statusText || e.status}`);
}
throw new Error(`Unable to retrieve manifest from ${manifestUrl}: ${e.message}`);
throw new Error(`Unable to retrieve data from ${endpointUrl}: ${e.message}`);
}
}

Expand Down Expand Up @@ -329,6 +336,15 @@ export class EMSClient {
return await this._loadTMSServices();
}

cacheGeoJson(layerId: string, geoJson: FeatureCollection): void {
this._cache.set(layerId, geoJson);
return;
}

getCachedGeoJson(layerId: string) {
return this._cache.get(layerId);
}

getTileApiUrl(): string {
return this._tileApiUrl;
}
Expand Down Expand Up @@ -406,6 +422,7 @@ export class EMSClient {
}

private _invalidateSettings(): void {
this._cache.reset();
this._getMainCatalog = _.once(
async (): Promise<EmsCatalogManifest> => {
// Preserve manifestServiceUrl parameter for backwards compatibility with EMS v7.2
Expand Down
34 changes: 33 additions & 1 deletion src/file_layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
FileLayerConfig,
} from './ems_client';
import { AbstractEmsService } from './ems_service';
import { FeatureCollection } from 'geojson';
import * as topojson from 'topojson-client';

export enum EMSFormatType {
geojson = 'geojson',
Expand All @@ -42,6 +44,34 @@ export class FileLayer extends AbstractEmsService {
this._config = config;
}

async getGeoJson(): Promise<FeatureCollection | undefined> {
const cachedGeoJson = this._emsClient.getCachedGeoJson(this.getId());
if (cachedGeoJson) {
return cachedGeoJson;
}

const format = this.getDefaultFormatType();
const fetchUrl = this.getDefaultFormatUrl();
// let fetchedJson;
let geojson;
const fetchedJson = await this._emsClient.getManifest(fetchUrl);
if (fetchedJson) {
if (format === 'geojson') {
geojson = (fetchedJson as unknown) as FeatureCollection;
} else if (format === 'topojson') {
const meta = this.getDefaultFormatMeta();
const featureCollectionPath = meta?.feature_collection_path ?? 'data';
// @ts-expect-error see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52156
geojson = topojson.feature(fetchedJson, featureCollectionPath) as FeatureCollection;
} else {
return;
}
this._emsClient.cacheGeoJson(this.getId(), geojson);
return geojson;
}
return;
}

getFields(): FileLayerConfig['fields'] {
return this._config.fields;
}
Expand Down Expand Up @@ -96,7 +126,9 @@ export class FileLayer extends AbstractEmsService {
return this._getFormatMeta(format);
}

getFormatOfTypeMeta(type: EMSFormatTypeStrings): { [key: string]: string } | undefined {
getFormatOfTypeMeta(
type: EMSFormatTypeStrings
): { [key: string]: string | undefined; feature_collection_path?: string } | undefined {
const format = this._getFormatOfType(type);
return this._getFormatMeta(format);
}
Expand Down
83 changes: 83 additions & 0 deletions test/__snapshots__/ems_client.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.getFileLayers[1] - getGeoJson defaults should convert topojson to geojson 1`] = `
Object {
"features": Array [
Object {
"geometry": Object {
"coordinates": Array [
Array [
Array [
72.77144284428442,
62.593,
],
Array [
50.625,
50.06444104410441,
],
Array [
91.7565793579358,
35.46,
],
Array [
93.867,
58.2621401140114,
],
Array [
72.77144284428442,
62.593,
],
],
],
"type": "Polygon",
},
"properties": Object {
"foo": "bar",
},
"type": "Feature",
},
],
"type": "FeatureCollection",
}
`;

exports[`.getFileLayers[1] - getGeoJson limited cache 1`] = `
Object {
"features": Array [
Object {
"geometry": Object {
"coordinates": Array [
Array [
Array [
72.77144284428442,
62.593,
],
Array [
50.625,
50.06444104410441,
],
Array [
91.7565793579358,
35.46,
],
Array [
93.867,
58.2621401140114,
],
Array [
72.77144284428442,
62.593,
],
],
],
"type": "Polygon",
},
"properties": Object {
"foo": "bar",
},
"type": "Feature",
},
],
"type": "FeatureCollection",
}
`;
Loading