ogcopenapis 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -27,7 +27,7 @@ npm run build
27
27
 
28
28
  ## To test
29
29
  Some test have been added that run in nodejs. No browser test is available at the moment.
30
- To run the test make sure you are using nodejs > 18 as function fetch is required.
30
+ Run uses isomorphic-fetch to provide fetch in node testing with jest.
31
31
  ```
32
32
  npm run test
33
33
  ```
@@ -0,0 +1,9 @@
1
+ declare class OgcOpenApiCrsTools {
2
+ /**
3
+ *
4
+ * @param uriOrName
5
+ * @private
6
+ */
7
+ static getReferenceName(uriOrName: string): string;
8
+ }
9
+ export { OgcOpenApiCrsTools };
@@ -0,0 +1,32 @@
1
+ class OgcOpenApiCrsTools {
2
+ /**
3
+ *
4
+ * @param uriOrName
5
+ * @private
6
+ */
7
+ static getReferenceName(uriOrName) {
8
+ if (!uriOrName.startsWith('http')) {
9
+ return uriOrName;
10
+ }
11
+ const prefix = 'http://www.opengis.net/def/crs/';
12
+ if (uriOrName.startsWith(prefix)) {
13
+ const cleanCRS = uriOrName.substr(prefix.length);
14
+ const parts = cleanCRS.split('/');
15
+ if (parts.length === 3) {
16
+ const name = parts[0];
17
+ const version = parts[1];
18
+ const code = parts[2];
19
+ switch (name) {
20
+ case 'EPSG':
21
+ return name + ':' + code;
22
+ break;
23
+ case 'OGC':
24
+ return 'CRS' + ':' + code.substr('CRS'.length);
25
+ break;
26
+ }
27
+ }
28
+ }
29
+ return uriOrName;
30
+ }
31
+ }
32
+ export { OgcOpenApiCrsTools };
@@ -0,0 +1,86 @@
1
+ import { Store } from '@luciad/ria/model/store/Store';
2
+ import { Cursor } from '@luciad/ria/model/Cursor';
3
+ import { Feature } from '@luciad/ria/model/feature/Feature';
4
+ import { Evented, Handle } from '@luciad/ria/util/Evented';
5
+ import { Bounds } from '@luciad/ria/shape/Bounds';
6
+ import { Codec, CodecDecodeOptions } from '@luciad/ria/model/codec/Codec';
7
+ import { CoordinateReference } from '@luciad/ria/reference/CoordinateReference';
8
+ interface OAPIFeatureStoreConstructorOptions {
9
+ dataUrl: string;
10
+ featureUrl?: string;
11
+ outputFormat: string;
12
+ useCrs84Bounds: boolean;
13
+ codec: Codec;
14
+ requestHeaders?: {
15
+ [key: string]: string;
16
+ };
17
+ customCrs: string;
18
+ extent?: {
19
+ spatial: {
20
+ bbox: any[];
21
+ };
22
+ };
23
+ reference: CoordinateReference;
24
+ }
25
+ interface OAPIFeatureStoreQueryOptions {
26
+ f?: string;
27
+ limit?: number;
28
+ offset?: number;
29
+ bbox?: number[];
30
+ "bbox-crs"?: string;
31
+ datetime?: string;
32
+ filter?: string;
33
+ "filter-crs"?: string;
34
+ "filter-lang"?: string;
35
+ crs?: string | any;
36
+ skipGeometry?: boolean;
37
+ properties?: string[];
38
+ sortby?: string[];
39
+ ident?: string;
40
+ name?: string;
41
+ q?: string[];
42
+ maxAllowableOffset?: number;
43
+ level?: string;
44
+ sector?: string;
45
+ milCode?: string;
46
+ localType?: string;
47
+ upperLimitDesc?: number;
48
+ upperLimitValue?: number;
49
+ upperLimitUnit?: string;
50
+ upperLimitCode?: string;
51
+ lowerLimitDesc?: string;
52
+ lowerLimitValue?: number;
53
+ lowerLimitUnit?: string;
54
+ lowerLimitCode?: string;
55
+ onshore?: boolean;
56
+ exclusion?: boolean;
57
+ wkhrCode?: string;
58
+ wkhrRemark?: string;
59
+ }
60
+ interface OgcOpenApiCodecDecodeOptions extends CodecDecodeOptions {
61
+ contentCrs?: string;
62
+ contentLength?: number;
63
+ }
64
+ export declare class OgcOpenApiFeatureStore implements Store, Evented {
65
+ private dataUrl;
66
+ private codec;
67
+ private eventedSupport;
68
+ private baseUrl;
69
+ private dataFormat;
70
+ private customCrs;
71
+ private bounds;
72
+ private requestHeaders;
73
+ private reference;
74
+ private outputFormat;
75
+ private useCrs84Bounds;
76
+ private featureUrl;
77
+ constructor(options: OAPIFeatureStoreConstructorOptions);
78
+ on(event: string, callback: any, context: any, options: any): Handle;
79
+ get(id: number | string, optionsInput?: any): Promise<Feature>;
80
+ query(receivedQuery?: OAPIFeatureStoreQueryOptions, options?: any): Cursor | Promise<Cursor>;
81
+ spatialQuery(bounds?: Bounds, receivedQuery?: OAPIFeatureStoreQueryOptions, options?: any): Promise<Cursor> | Cursor;
82
+ private static getCRS84BoundingBox;
83
+ private static cleanUrl;
84
+ private static dataFormatInQuery;
85
+ }
86
+ export { OgcOpenApiCodecDecodeOptions };
@@ -0,0 +1,206 @@
1
+ import { EventedSupport } from '@luciad/ria/util/EventedSupport';
2
+ import { createBounds } from '@luciad/ria/shape/ShapeFactory';
3
+ import { getReference } from "@luciad/ria/reference/ReferenceProvider";
4
+ import { createTransformation } from "@luciad/ria/transformation/TransformationFactory";
5
+ export class OgcOpenApiFeatureStore {
6
+ constructor(options) {
7
+ // console.log(options);
8
+ this.useCrs84Bounds = typeof options.useCrs84Bounds !== "undefined" ? options.useCrs84Bounds : false;
9
+ this.dataUrl = options.dataUrl;
10
+ this.outputFormat = options.outputFormat;
11
+ const predictedFeatureUrl = this.dataUrl.replace(/\.[^/.]+$/, "");
12
+ this.featureUrl = options.featureUrl ? OgcOpenApiFeatureStore.cleanUrl(options.featureUrl) : OgcOpenApiFeatureStore.cleanUrl(predictedFeatureUrl);
13
+ this.baseUrl = OgcOpenApiFeatureStore.cleanUrl(this.dataUrl);
14
+ this.dataFormat = OgcOpenApiFeatureStore.dataFormatInQuery(this.dataUrl);
15
+ this.customCrs = options.customCrs;
16
+ this.codec = options.codec;
17
+ this.eventedSupport = new EventedSupport();
18
+ this.requestHeaders = options.requestHeaders ? options.requestHeaders : {};
19
+ this.reference = options.reference;
20
+ if (options.extent &&
21
+ options.extent.spatial &&
22
+ options.extent.spatial.bbox &&
23
+ options.extent.spatial.bbox.length > 0) {
24
+ const bbox = options.extent.spatial.bbox[0];
25
+ // console.log(options.extent.spatial);
26
+ this.bounds = createBounds(this.reference, [
27
+ bbox[0],
28
+ bbox[2] - bbox[0],
29
+ bbox[1],
30
+ bbox[3] - bbox[1],
31
+ ]);
32
+ }
33
+ }
34
+ on(event, callback, context, options) {
35
+ return this.eventedSupport.on(event, callback, context, options);
36
+ }
37
+ get(id, optionsInput) {
38
+ return new Promise((resolve) => {
39
+ const options = optionsInput ? Object.assign({}, optionsInput) : {};
40
+ options.query = (options.query ? options.query : {});
41
+ options.query.f = options.query.f ? options.query.f : this.dataFormat;
42
+ options.query.crs = options.query.crs ? options.query.crs : this.customCrs;
43
+ // Mod starta
44
+ const itemsAccept = typeof this.requestHeaders.accept !== 'undefined'
45
+ ? this.requestHeaders.accept.split(';')
46
+ : typeof this.requestHeaders.Accept !== 'undefined'
47
+ ? this.requestHeaders.Accept.split(';')
48
+ : [];
49
+ itemsAccept.push(this.outputFormat);
50
+ const accept = itemsAccept.join(';');
51
+ // Mod end
52
+ let request = this.featureUrl + id;
53
+ const simpleQuery = [];
54
+ if (options.query.f)
55
+ simpleQuery.push("f");
56
+ if (options.query.crs)
57
+ simpleQuery.push("crs");
58
+ if (simpleQuery.length > 0) {
59
+ const queryString = simpleQuery.map(k => "" + k + "=" + options.query[k]).join("&");
60
+ request += "?" + queryString;
61
+ }
62
+ const headers = Object.assign(Object.assign({}, this.requestHeaders), { Accept: accept });
63
+ fetch(request, {
64
+ method: 'GET',
65
+ headers,
66
+ }).then((response) => {
67
+ if (response.status === 200) {
68
+ response.text().then((content) => {
69
+ let contentType = response.headers.get('Content-Type');
70
+ const contentCrs = response.headers.get('content-crs');
71
+ const contentLengthStr = response.headers.get('Content-Length');
72
+ const contentLength = Number(contentLengthStr);
73
+ contentType = contentType ? contentType : 'text/plain';
74
+ content = content
75
+ .split('http://www.opengis.net/def/crs/OGC/1.3/CRS84')
76
+ .join('urn:ogc:def:crs:OGC:1.3:CRS84');
77
+ const codecOptions = {
78
+ content, contentType, contentCrs, contentLength
79
+ };
80
+ const cursor = this.codec.decode(codecOptions);
81
+ if (cursor.hasNext()) {
82
+ const feaure = cursor.next();
83
+ resolve(feaure);
84
+ }
85
+ else {
86
+ resolve(null);
87
+ }
88
+ });
89
+ }
90
+ });
91
+ });
92
+ }
93
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
94
+ query(receivedQuery, options) {
95
+ return new Promise((resolve) => {
96
+ const query = receivedQuery ? Object.assign({}, receivedQuery) : {};
97
+ query.f = query.f ? query.f : this.dataFormat;
98
+ query.crs = query.crs ? query.crs : this.customCrs;
99
+ query.limit = query.limit ? query.limit : undefined;
100
+ let request = this.baseUrl;
101
+ // Mod starta
102
+ const itemsAccept = typeof this.requestHeaders.accept !== 'undefined'
103
+ ? this.requestHeaders.accept.split(';')
104
+ : typeof this.requestHeaders.Accept !== 'undefined'
105
+ ? this.requestHeaders.Accept.split(';')
106
+ : [];
107
+ itemsAccept.push(this.outputFormat);
108
+ const accept = itemsAccept.join(';');
109
+ // Mod end
110
+ const allKeys = Object.keys(query);
111
+ if (allKeys.length > 0) {
112
+ const filteredKeys = allKeys.filter(e => (typeof query[e] !== "undefined" && query[e] !== null));
113
+ const indexOfFormat = filteredKeys.indexOf("f");
114
+ if (indexOfFormat > -1) {
115
+ filteredKeys.splice(indexOfFormat, 1);
116
+ filteredKeys.unshift("f");
117
+ }
118
+ const queryString = filteredKeys.map(k => "" + k + "=" + query[k]).join("&");
119
+ request += "?" + queryString;
120
+ }
121
+ const headers = Object.assign(Object.assign({}, this.requestHeaders), { Accept: accept });
122
+ // const headers = { ...this.requestHeaders };
123
+ fetch(request, {
124
+ method: 'GET',
125
+ headers,
126
+ }).then((response) => {
127
+ if (response.status === 200) {
128
+ response.text().then((content) => {
129
+ let contentType = response.headers.get('Content-Type');
130
+ const contentCrs = response.headers.get('content-crs');
131
+ const contentLengthStr = response.headers.get('Content-Length');
132
+ const contentLength = Number(contentLengthStr);
133
+ contentType = contentType ? contentType : 'text/plain';
134
+ content = content
135
+ .split('srsName="http://www.opengis.net/def/crs/OGC/1.3/CRS84"')
136
+ .join('srsName="urn:ogc:def:crs:OGC:1.3:CRS84"');
137
+ const codecOptions = {
138
+ content, contentType, contentCrs, contentLength
139
+ };
140
+ const cursor = this.codec.decode(codecOptions);
141
+ resolve(cursor);
142
+ });
143
+ }
144
+ });
145
+ });
146
+ }
147
+ spatialQuery(bounds, receivedQuery, options) {
148
+ const query = (receivedQuery ? Object.assign({}, receivedQuery) : {});
149
+ delete query.bbox;
150
+ query.limit = query.limit ? query.limit : undefined;
151
+ if (bounds) {
152
+ if (this.useCrs84Bounds) {
153
+ const crs84Bounds = OgcOpenApiFeatureStore.getCRS84BoundingBox(bounds);
154
+ query.bbox = query.bbox
155
+ ? query.bbox
156
+ : [
157
+ crs84Bounds.x,
158
+ crs84Bounds.y,
159
+ crs84Bounds.x + crs84Bounds.width,
160
+ crs84Bounds.y + crs84Bounds.height,
161
+ ];
162
+ }
163
+ else {
164
+ query.bbox = query.bbox
165
+ ? query.bbox
166
+ : [
167
+ bounds.x,
168
+ bounds.y,
169
+ bounds.x + bounds.width,
170
+ bounds.y + bounds.height,
171
+ ];
172
+ if (this.customCrs) {
173
+ query["bbox-crs"] = this.customCrs;
174
+ }
175
+ }
176
+ }
177
+ return this.query(query, options);
178
+ }
179
+ static getCRS84BoundingBox(shape) {
180
+ const WGS84 = getReference("CRS:84");
181
+ const bounds = shape.bounds;
182
+ const toWgs84 = createTransformation(bounds.reference, WGS84);
183
+ const newBounds = toWgs84.transformBounds(bounds);
184
+ return newBounds;
185
+ }
186
+ static cleanUrl(dataUrl) {
187
+ const parts = dataUrl.split('?');
188
+ const cleanUrl = parts[0].endsWith('/') ? parts[0] : parts[0] + '/';
189
+ return cleanUrl;
190
+ }
191
+ static dataFormatInQuery(dataUrl) {
192
+ let format;
193
+ const parts = dataUrl.split('?');
194
+ if (parts.length > 1) {
195
+ const query = parts[1];
196
+ const qs = query.split('&');
197
+ for (const q of qs) {
198
+ if (q.startsWith('f=')) {
199
+ format = q.substr(2);
200
+ return format;
201
+ }
202
+ }
203
+ }
204
+ return format;
205
+ }
206
+ }
@@ -0,0 +1,31 @@
1
+ interface ExtFetchOptions {
2
+ originalUrl: string;
3
+ complete?: (url: string) => string;
4
+ proxify?: (url: string) => {
5
+ url: string;
6
+ fetchOptions: RequestInit;
7
+ };
8
+ }
9
+ export interface FetchServerOptions {
10
+ proxy: (url: string, fetchOptions: any) => {
11
+ fetchOptions: any;
12
+ url: string;
13
+ };
14
+ hostname: string;
15
+ complete: (url: string) => string;
16
+ }
17
+ declare class OgcOpenApiFetchTools {
18
+ static createFetchOptions(options: ExtFetchOptions): {
19
+ hostname: string;
20
+ complete: (url: string) => string;
21
+ proxy: (url: string, fetchOptions: any) => {
22
+ url: string;
23
+ fetchOptions: any;
24
+ };
25
+ };
26
+ static fetch(input: string, options: RequestInit, serverOptions: FetchServerOptions): Promise<Response>;
27
+ static addHostURL(url: string, HostUrl?: string): string;
28
+ static cleanUrl(inputUrl: string): string;
29
+ static getHostURL(fullUrl: string): string;
30
+ }
31
+ export { OgcOpenApiFetchTools };
@@ -0,0 +1,46 @@
1
+ class OgcOpenApiFetchTools {
2
+ static createFetchOptions(options) {
3
+ const hostname = this.getHostURL(options.originalUrl);
4
+ return {
5
+ hostname: hostname,
6
+ complete: typeof options.complete === "function" ? options.complete : (url) => {
7
+ return this.addHostURL(url, hostname);
8
+ },
9
+ proxy: typeof options.proxify === "function" ? options.proxify : (url, fetchOptions) => ({
10
+ url: url,
11
+ fetchOptions: fetchOptions
12
+ })
13
+ };
14
+ }
15
+ static fetch(input, options, serverOptions) {
16
+ const fullUrl = serverOptions.complete(input);
17
+ const result = serverOptions.proxy(fullUrl, options);
18
+ return fetch(result.url, result.fetchOptions);
19
+ }
20
+ static addHostURL(url, HostUrl) {
21
+ const hostUrl = HostUrl ? HostUrl : "";
22
+ if (url.startsWith("http://") || url.startsWith("https://")) {
23
+ return url;
24
+ }
25
+ else {
26
+ if (url.startsWith("/"))
27
+ return hostUrl + url;
28
+ if (url.startsWith("./"))
29
+ return hostUrl + url.substring(1);
30
+ }
31
+ return hostUrl + "/" + url;
32
+ }
33
+ static cleanUrl(inputUrl) {
34
+ const url = inputUrl.split("?")[0].replace(/\/?$/, '/');
35
+ return url;
36
+ }
37
+ static getHostURL(fullUrl) {
38
+ if (!fullUrl.startsWith("http"))
39
+ return "";
40
+ const pathArray = fullUrl.split('/');
41
+ const protocol = pathArray[0];
42
+ const host = pathArray[2];
43
+ return protocol + '//' + host;
44
+ }
45
+ }
46
+ export { OgcOpenApiFetchTools };
@@ -0,0 +1,114 @@
1
+ import { FetchServerOptions } from "./OgcOpenApiFetchTools";
2
+ type RequestHeaders = {
3
+ [headerName: string]: string;
4
+ } | null;
5
+ export interface LinkType {
6
+ rel: string;
7
+ type: string;
8
+ title: string;
9
+ href: string;
10
+ }
11
+ interface TileMatrix {
12
+ id: string;
13
+ scaleDenominator: number;
14
+ cellSize: number;
15
+ cornerOfOrigin: string;
16
+ pointOfOrigin: number[];
17
+ matrixWidth: number;
18
+ matrixHeight: number;
19
+ tileWidth: number;
20
+ tileHeight: number;
21
+ variableMatrixWidths: {
22
+ coalesce: number;
23
+ minTileRow: number;
24
+ maxTileRow: number;
25
+ }[];
26
+ }
27
+ export interface TileSetData {
28
+ id: string;
29
+ title: string;
30
+ uri: string;
31
+ crs: string;
32
+ orderedAxes: string[];
33
+ tileMatrices: TileMatrix[];
34
+ boundingBox: {
35
+ lowerLeft: number[];
36
+ upperRight: number[];
37
+ };
38
+ }
39
+ export interface TileSetMeta {
40
+ id: string;
41
+ title: string;
42
+ links: LinkType[];
43
+ }
44
+ export interface OgcOpenApiCapabilitiesObject {
45
+ name: string;
46
+ collections: OgcOpenApiCapabilitiesCollection[];
47
+ version: string;
48
+ service: string;
49
+ info: any;
50
+ crs?: any[];
51
+ baseUrl: string;
52
+ serverOptions: FetchServerOptions;
53
+ }
54
+ export interface OgcOpenApiCapabilitiesCollection {
55
+ id: string;
56
+ name: string;
57
+ title: string;
58
+ defaultReference: string;
59
+ outputFormats: string[];
60
+ description: string;
61
+ keywords: string[];
62
+ links: OgcOpenApiCapabilitiesCollectionServiceLinkType[];
63
+ extent: any;
64
+ crs?: any;
65
+ storageCrs?: any;
66
+ }
67
+ export interface OgcOpenApiCapabilitiesCollectionServiceLinkType {
68
+ rel: string;
69
+ type: string;
70
+ title: string;
71
+ href: string;
72
+ }
73
+ export declare enum CollectionLinkType {
74
+ Items = "items",
75
+ Styles = "styles",
76
+ Map = "map",
77
+ Tiles = "tiles"
78
+ }
79
+ export interface OgcOpenApiGetCapabilitiesFromURL {
80
+ filterCollectionsByLinkType?: CollectionLinkType;
81
+ credentials?: boolean;
82
+ requestHeaders?: {
83
+ [headerName: string]: string;
84
+ } | null;
85
+ proxify?: (url: string) => {
86
+ url: string;
87
+ fetchOptions: RequestInit;
88
+ };
89
+ }
90
+ export declare class OgcOpenApiGetCapabilities {
91
+ static fromURL(inputRequest: string, options?: OgcOpenApiGetCapabilitiesFromURL): Promise<OgcOpenApiCapabilitiesObject>;
92
+ private static fetchLinkContentAsJSON;
93
+ static filterCollectionLinks(links: OgcOpenApiCapabilitiesCollectionServiceLinkType[], linkType: CollectionLinkType): OgcOpenApiCapabilitiesCollectionServiceLinkType[];
94
+ static getDataLink(currentLayer: OgcOpenApiCapabilitiesCollection, preferedFormat: string): string;
95
+ private static detectTypeFormat;
96
+ static fetchTileSetsInFull(capabilities: OgcOpenApiCapabilitiesObject | null, options?: {
97
+ credentials?: boolean;
98
+ requestHeaders?: RequestHeaders;
99
+ }): Promise<TileSetData[]>;
100
+ static getQuadTreeCompatibleLevelOffset(tileMatrixSet: TileSetData): number;
101
+ static fetchTileSets(capabilities: OgcOpenApiCapabilitiesObject | null, options?: {
102
+ credentials?: boolean;
103
+ requestHeaders?: RequestHeaders;
104
+ }): Promise<TileSetMeta[]>;
105
+ static getTilesLink(capabilities: OgcOpenApiCapabilitiesObject | null, currentCollection: OgcOpenApiCapabilitiesCollection, tileMatrixId: string, options?: {
106
+ credentials?: boolean;
107
+ requestHeaders?: RequestHeaders;
108
+ }): Promise<LinkType[]>;
109
+ static fetchTileset(capabilities: OgcOpenApiCapabilitiesObject | null, tileset: TileSetMeta, options?: {
110
+ credentials?: boolean;
111
+ requestHeaders?: RequestHeaders;
112
+ }): Promise<TileSetData>;
113
+ }
114
+ export {};
@@ -0,0 +1,276 @@
1
+ import { OgcOpenApiFetchTools } from "./OgcOpenApiFetchTools";
2
+ const SCALE_DENOMINATOR_TOLERANCE = 1e-8;
3
+ export var CollectionLinkType;
4
+ (function (CollectionLinkType) {
5
+ CollectionLinkType["Items"] = "items";
6
+ CollectionLinkType["Styles"] = "styles";
7
+ CollectionLinkType["Map"] = "map";
8
+ CollectionLinkType["Tiles"] = "tiles";
9
+ })(CollectionLinkType || (CollectionLinkType = {}));
10
+ const CollectionLinkTypeAlternatives = {
11
+ [CollectionLinkType.Items]: ["items", "item"],
12
+ [CollectionLinkType.Styles]: ["http://www.opengis.net/def/rel/ogc/1.0/styles", "styles"],
13
+ [CollectionLinkType.Map]: ["http://www.opengis.net/def/rel/ogc/1.0/map", "map"],
14
+ [CollectionLinkType.Tiles]: ["http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", "tilesets-map"],
15
+ };
16
+ export class OgcOpenApiGetCapabilities {
17
+ static fromURL(inputRequest, options) {
18
+ return new Promise((resolve, reject) => {
19
+ const baseUrl = OgcOpenApiFetchTools.cleanUrl(inputRequest);
20
+ const requestUrl = OgcOpenApiFetchTools.cleanUrl(inputRequest);
21
+ const serverOptions = OgcOpenApiFetchTools.createFetchOptions({ originalUrl: requestUrl, proxify: options === null || options === void 0 ? void 0 : options.proxify });
22
+ OgcOpenApiFetchTools.fetch(requestUrl, {
23
+ method: 'GET',
24
+ credentials: (options === null || options === void 0 ? void 0 : options.credentials) ? "same-origin" : "omit",
25
+ headers: options ? options.requestHeaders : {},
26
+ }, serverOptions).then((response) => {
27
+ if (response.status === 200) {
28
+ response.json().then((jsonObject) => {
29
+ if (!jsonObject.links) {
30
+ reject("Invalid format: (property 'links' is missing)");
31
+ return;
32
+ }
33
+ let linkToData = jsonObject.links.find((link) => link.rel === 'data' && link.type === 'application/json');
34
+ if (!linkToData) {
35
+ linkToData = jsonObject.links.find((link) => link.rel === 'data');
36
+ }
37
+ const linkToApi = jsonObject.links.find((link) => {
38
+ return ((link.rel === 'service-desc' || link.rel === 'service') &&
39
+ link.type.indexOf('openapi+json') > -1);
40
+ });
41
+ const promiseArray = [];
42
+ if (linkToData) {
43
+ const collectionsPromise = OgcOpenApiGetCapabilities.fetchLinkContentAsJSON(linkToData, { credentials: options.credentials, requestHeaders: options.requestHeaders }, serverOptions);
44
+ promiseArray.push(collectionsPromise);
45
+ if (linkToApi) {
46
+ const apiPromise = OgcOpenApiGetCapabilities.fetchLinkContentAsJSON(linkToApi, { credentials: options.credentials, requestHeaders: options.requestHeaders }, serverOptions);
47
+ promiseArray.push(apiPromise);
48
+ }
49
+ Promise.all(promiseArray).then((responses) => {
50
+ const responseDataLink = responses[0];
51
+ const responseOpenApi = responses.length >= 1 ? responses[1] : undefined;
52
+ const crsArray = typeof responseDataLink.crs !== "undefined" ? { crs: responseDataLink.crs } : {};
53
+ const foundCollections = responseDataLink.collections.filter(c => options && options.filterCollectionsByLinkType ?
54
+ OgcOpenApiGetCapabilities.filterCollectionLinks(c.links, options.filterCollectionsByLinkType).length > 0 : true)
55
+ .map((collection) => {
56
+ const name = typeof collection.id !== 'undefined'
57
+ ? collection.id
58
+ : collection.name;
59
+ const collectionCrsObject = {};
60
+ if (typeof collection.crs !== "undefined")
61
+ collectionCrsObject.crs = collection.crs;
62
+ if (typeof collection.storageCrs !== "undefined")
63
+ collectionCrsObject.storageCrs = collection.storageCrs;
64
+ const layer = Object.assign({ description: collection.description, id: name, name, title: collection.title, keywords: collection.keywords
65
+ ? collection.keywords
66
+ : [], defaultReference: 'CRS:84', links: collection.links, outputFormats: options.filterCollectionsByLinkType ?
67
+ this.filterCollectionLinks(collection.links, options.filterCollectionsByLinkType).map(link => this.detectTypeFormat(link)) :
68
+ collection.links.map(link => this.detectTypeFormat(link)), extent: collection.extent }, collectionCrsObject);
69
+ return layer;
70
+ });
71
+ // console.log(featureTypes);
72
+ const o = Object.assign(Object.assign({ name: '', collections: foundCollections, version: responseOpenApi ? responseOpenApi.openapi : '', service: '', info: responseOpenApi ? responseOpenApi.info : {} }, crsArray), { serverOptions: serverOptions, baseUrl });
73
+ resolve(o);
74
+ });
75
+ }
76
+ else {
77
+ reject("Invalid format: (property 'links' contains no links with rel=data");
78
+ }
79
+ }, () => {
80
+ reject("Not JSON");
81
+ });
82
+ }
83
+ else {
84
+ reject(response.status);
85
+ }
86
+ }, (err) => {
87
+ reject(err);
88
+ });
89
+ });
90
+ }
91
+ static fetchLinkContentAsJSON(link, options, serverOptions) {
92
+ return new Promise((resolve) => {
93
+ OgcOpenApiFetchTools.fetch(link.href, { method: "GET", credentials: options.credentials ? "same-origin" : "omit", headers: options.requestHeaders }, serverOptions).then((result) => {
94
+ if (result.status === 200) {
95
+ result.json().then((json) => resolve(json));
96
+ }
97
+ else {
98
+ resolve(undefined);
99
+ }
100
+ }, () => {
101
+ resolve(undefined);
102
+ });
103
+ });
104
+ }
105
+ static filterCollectionLinks(links, linkType) {
106
+ switch (linkType) {
107
+ case CollectionLinkType.Items:
108
+ case CollectionLinkType.Map:
109
+ case CollectionLinkType.Styles:
110
+ case CollectionLinkType.Tiles:
111
+ return links.filter((link) => CollectionLinkTypeAlternatives[linkType].some(e => e === link.rel));
112
+ default:
113
+ return [];
114
+ }
115
+ }
116
+ static getDataLink(currentLayer, preferedFormat) {
117
+ const dataLink = OgcOpenApiGetCapabilities.filterCollectionLinks(currentLayer.links, CollectionLinkType.Items).find((link) => link.type === preferedFormat);
118
+ return dataLink ? dataLink.href : '';
119
+ }
120
+ static detectTypeFormat(link) {
121
+ if (link.type)
122
+ return link.type;
123
+ let rel = null;
124
+ for (let key in CollectionLinkTypeAlternatives) {
125
+ if (CollectionLinkTypeAlternatives.hasOwnProperty(key)) {
126
+ CollectionLinkTypeAlternatives[key].some(e => e === link.rel);
127
+ rel = key;
128
+ break;
129
+ }
130
+ }
131
+ if (rel) {
132
+ if (CollectionLinkType.Map)
133
+ return "image/png";
134
+ }
135
+ return "application/json";
136
+ }
137
+ static fetchTileSetsInFull(capabilities, options) {
138
+ return new Promise((resolve, reject) => {
139
+ this.fetchTileSets(capabilities, options).then((tileSetMetaArray) => {
140
+ const promises = tileSetMetaArray.map(tml => this.fetchTileset(capabilities, tml, options));
141
+ Promise.all(promises).then((arr) => {
142
+ resolve(arr);
143
+ });
144
+ });
145
+ });
146
+ }
147
+ static getQuadTreeCompatibleLevelOffset(tileMatrixSet) {
148
+ const levels = tileMatrixSet.tileMatrices;
149
+ const reference = levels[levels.length - 1];
150
+ for (let level = levels.length - 2; level >= 0; level--) {
151
+ const current = levels[level];
152
+ // tslint:disable-next-line:no-bitwise
153
+ const multiplier = 1 << (levels.length - level - 1);
154
+ const errorMargin = current.scaleDenominator * SCALE_DENOMINATOR_TOLERANCE;
155
+ if (Math.abs(current.scaleDenominator - reference.scaleDenominator * multiplier) > errorMargin) {
156
+ return level + 1;
157
+ }
158
+ if (current.pointOfOrigin[0] !== reference.pointOfOrigin[0]) {
159
+ return level + 1;
160
+ }
161
+ if (current.tileWidth !== reference.tileWidth || current.tileHeight !== reference.tileHeight) {
162
+ return level + 1;
163
+ }
164
+ if (typeof current.variableMatrixWidths !== "undefined") {
165
+ return level + 1;
166
+ }
167
+ if (current.matrixWidth !== (reference.matrixWidth / multiplier) || current.matrixHeight !== (reference.matrixHeight / multiplier)) {
168
+ return level + 1;
169
+ }
170
+ }
171
+ return 0;
172
+ }
173
+ static fetchTileSets(capabilities, options) {
174
+ return new Promise((resolve, reject) => {
175
+ if (capabilities === null)
176
+ reject();
177
+ const url = capabilities.baseUrl + "tileMatrixSets";
178
+ OgcOpenApiFetchTools.fetch(url, {
179
+ method: "GET",
180
+ credentials: (options === null || options === void 0 ? void 0 : options.credentials) ? "same-origin" : "omit",
181
+ headers: (options === null || options === void 0 ? void 0 : options.requestHeaders) ? options.requestHeaders : {}
182
+ }, capabilities.serverOptions).then(result => {
183
+ if (result.status === 200) {
184
+ result.json().then((data) => {
185
+ if (data.tileMatrixSets) {
186
+ resolve(data.tileMatrixSets);
187
+ }
188
+ else {
189
+ reject({ error: true, message: "tileMatrixSets not defined" });
190
+ }
191
+ }, () => {
192
+ reject({ error: true, message: "Not JSON data" });
193
+ });
194
+ }
195
+ else {
196
+ reject({ error: true, message: "Invalid response: " + result.status });
197
+ }
198
+ }, (err) => {
199
+ reject({ error: true, message: err.message });
200
+ });
201
+ });
202
+ }
203
+ static getTilesLink(capabilities, currentCollection, tileMatrixId, options) {
204
+ return new Promise((resolve, reject) => {
205
+ if (capabilities === null)
206
+ reject();
207
+ const dataLinks = OgcOpenApiGetCapabilities.filterCollectionLinks(currentCollection.links, CollectionLinkType.Tiles);
208
+ const link = dataLinks.find((link) => link.type === "application/json");
209
+ const href = link ? link.href : dataLinks.length > 0 ? dataLinks[0].href : '';
210
+ const tileInfoUrl = OgcOpenApiFetchTools.cleanUrl(href) + tileMatrixId;
211
+ OgcOpenApiFetchTools.fetch(tileInfoUrl, {
212
+ method: "GET",
213
+ credentials: (options === null || options === void 0 ? void 0 : options.credentials) ? "same-origin" : "omit",
214
+ headers: (options === null || options === void 0 ? void 0 : options.requestHeaders) ? options === null || options === void 0 ? void 0 : options.requestHeaders : {}
215
+ }, capabilities.serverOptions).then(result => {
216
+ if (result.status === 200) {
217
+ result.json().then((data) => {
218
+ if (data.links.length > 0) {
219
+ const links = data.links.filter(l => l.rel === "item" || l.rel === "items").map(a => (Object.assign(Object.assign({}, a), { href: capabilities.serverOptions.complete(a.href) })));
220
+ if (links) {
221
+ resolve(links);
222
+ }
223
+ else {
224
+ reject({ error: true, message: "Invalid" });
225
+ }
226
+ }
227
+ else {
228
+ reject({ error: true, message: "Invalid" });
229
+ }
230
+ }, () => {
231
+ reject({ error: true, message: "Not JSON data" });
232
+ });
233
+ }
234
+ else {
235
+ reject({ error: true, message: "Invalid response: " + result.status });
236
+ }
237
+ }, (err) => {
238
+ reject({ error: true, message: err.message });
239
+ });
240
+ });
241
+ }
242
+ static fetchTileset(capabilities, tileset, options) {
243
+ return new Promise((resolve, reject) => {
244
+ if (capabilities === null) {
245
+ reject();
246
+ return;
247
+ }
248
+ let jsonLink = tileset.links.find(l => l.type ? l.type === "application/json" : false);
249
+ jsonLink = jsonLink ? jsonLink : tileset.links[0];
250
+ const href = OgcOpenApiFetchTools.cleanUrl(jsonLink.href);
251
+ OgcOpenApiFetchTools.fetch(href, {
252
+ method: "GET",
253
+ credentials: (options === null || options === void 0 ? void 0 : options.credentials) ? "same-origin" : "omit",
254
+ headers: options ? options.requestHeaders : {}
255
+ }, capabilities.serverOptions).then(result => {
256
+ if (result.status === 200) {
257
+ result.json().then((data) => {
258
+ if (typeof data.id !== "undefined" && data.tileMatrices && data.crs) {
259
+ resolve(data);
260
+ }
261
+ else {
262
+ reject({ error: true, message: "TileSet has invalid data" });
263
+ }
264
+ }, () => {
265
+ reject({ error: true, message: "Not JSON data" });
266
+ });
267
+ }
268
+ else {
269
+ reject({ error: true, message: "Invalid response: " + result.status });
270
+ }
271
+ }, (err) => {
272
+ reject({ error: true, message: err.message });
273
+ });
274
+ });
275
+ }
276
+ }
@@ -0,0 +1,7 @@
1
+ import { OgcOpenApiCapabilitiesCollection, OgcOpenApiCapabilitiesObject } from "./OgcOpenApiGetCapabilities";
2
+ declare class OgcOpenApiGetCollectionTiles {
3
+ private collection;
4
+ private capabilities;
5
+ constructor(collection: OgcOpenApiCapabilitiesCollection, capabilities: OgcOpenApiCapabilitiesObject);
6
+ }
7
+ export { OgcOpenApiGetCollectionTiles };
@@ -0,0 +1,7 @@
1
+ class OgcOpenApiGetCollectionTiles {
2
+ constructor(collection, capabilities) {
3
+ this.collection = collection;
4
+ this.capabilities = capabilities;
5
+ }
6
+ }
7
+ export { OgcOpenApiGetCollectionTiles };
@@ -0,0 +1,32 @@
1
+ import { RasterDataType } from "@luciad/ria/model/tileset/RasterDataType";
2
+ import { RasterSamplingMode } from "@luciad/ria/model/tileset/RasterSamplingMode";
3
+ import { WMSTileSetModel } from "@luciad/ria/model/tileset/WMSTileSetModel";
4
+ import { TileCoordinate } from "@luciad/ria/model/tileset/TileCoordinate";
5
+ interface OgcOpenApiMapsModelConstructorOptions {
6
+ crs: string;
7
+ baseURL: string;
8
+ collection: string;
9
+ dataType?: RasterDataType;
10
+ samplingMode?: RasterSamplingMode;
11
+ requestHeaders?: {
12
+ [p: string]: string;
13
+ };
14
+ format?: string;
15
+ reverseAxis?: boolean;
16
+ datetime?: string;
17
+ subset?: string[];
18
+ transparent?: boolean;
19
+ bgcolor?: string;
20
+ }
21
+ declare class OgcOpenApiMapsModel extends WMSTileSetModel {
22
+ private format;
23
+ private crs;
24
+ private reverseAxis;
25
+ private datetime;
26
+ private subset;
27
+ private bgcolor;
28
+ constructor(o: OgcOpenApiMapsModelConstructorOptions);
29
+ getTileURL(baseURL: string, tile: TileCoordinate): string | null;
30
+ private static createURLParameters;
31
+ }
32
+ export { OgcOpenApiMapsModel };
@@ -0,0 +1,69 @@
1
+ import { WMSTileSetModel } from "@luciad/ria/model/tileset/WMSTileSetModel";
2
+ import { OgcOpenApiCrsTools } from "./OgcOpenApiCrsTools";
3
+ import { getReference } from "@luciad/ria/reference/ReferenceProvider";
4
+ class OgcOpenApiMapsModel extends WMSTileSetModel {
5
+ constructor(o) {
6
+ const referenceName = OgcOpenApiCrsTools.getReferenceName(o.crs);
7
+ const reference = getReference(referenceName);
8
+ super({
9
+ reference,
10
+ getMapRoot: o.baseURL,
11
+ layers: [o.collection],
12
+ dataType: o.dataType,
13
+ samplingMode: o.samplingMode,
14
+ requestHeaders: o.requestHeaders
15
+ });
16
+ this.crs = o.crs;
17
+ this.format = o.format;
18
+ this.reverseAxis = o.reverseAxis;
19
+ this.datetime = o.datetime;
20
+ this.subset = o.subset;
21
+ this.transparent = typeof o.transparent !== "undefined" ? o.transparent : true;
22
+ this.bgcolor = o.bgcolor;
23
+ this.modelDescriptor = {
24
+ source: o.baseURL,
25
+ name: o.collection,
26
+ description: "OGC Open API Maps",
27
+ type: super.dataType
28
+ };
29
+ }
30
+ getTileURL(baseURL, tile) {
31
+ let aURL = super.getTileURL(baseURL, tile);
32
+ if (aURL) {
33
+ const parts = aURL.split("?");
34
+ const queryString = parts.length > 1 ? parts[1] : "";
35
+ const url = parts[0];
36
+ const urlParams = new URLSearchParams(queryString);
37
+ const bbox = urlParams.get('BBOX');
38
+ const urlParameters = {
39
+ bbox: bbox,
40
+ "bbox-crs": this.crs,
41
+ crs: this.crs,
42
+ f: this.format,
43
+ datetime: this.datetime,
44
+ subset: this.subset,
45
+ transparent: this.transparent,
46
+ bgcolor: this.transparent ? undefined : this.bgcolor
47
+ };
48
+ const query = OgcOpenApiMapsModel.createURLParameters(urlParameters);
49
+ const transformedUrl = url + "?" + query;
50
+ return transformedUrl;
51
+ }
52
+ else {
53
+ return aURL;
54
+ }
55
+ }
56
+ static createURLParameters(obj) {
57
+ let str = Object.keys(obj).filter(k => obj[k] !== undefined).map(function (key) {
58
+ if (Array.isArray(obj[key])) {
59
+ return key + '=' + obj[key].join(",");
60
+ }
61
+ if (typeof obj[key] == "boolean") {
62
+ return key + '=' + (obj[key] ? "true" : "false");
63
+ }
64
+ return key + '=' + obj[key];
65
+ }).join('&');
66
+ return str;
67
+ }
68
+ }
69
+ export { OgcOpenApiMapsModel };
@@ -0,0 +1,28 @@
1
+ import { UrlTileSetModel } from "@luciad/ria/model/tileset/UrlTileSetModel";
2
+ import { RasterDataType } from "@luciad/ria/model/tileset/RasterDataType";
3
+ import { RasterSamplingMode } from "@luciad/ria/model/tileset/RasterSamplingMode";
4
+ import { TileCoordinate } from "@luciad/ria/model/tileset/TileCoordinate";
5
+ import { TileSetData } from "./OgcOpenApiGetCapabilities";
6
+ export interface OgcOpenApiTilesModelOptions {
7
+ tileMatrix: TileSetData;
8
+ baseURL: string;
9
+ collection: string;
10
+ dataType?: RasterDataType;
11
+ samplingMode?: RasterSamplingMode;
12
+ requestHeaders?: {
13
+ [p: string]: string;
14
+ };
15
+ transparent?: boolean;
16
+ bgcolor?: string;
17
+ }
18
+ export declare class OgcOpenApiTilesModel extends UrlTileSetModel {
19
+ private tileMatrix;
20
+ private invertY;
21
+ private plevel0Rows;
22
+ private transparent;
23
+ private bgcolor;
24
+ constructor(options: OgcOpenApiTilesModelOptions);
25
+ getTileURL(baseURL: string, tile: TileCoordinate): string | null;
26
+ debugAsImage(tileCorrected: TileCoordinate): string;
27
+ private static createURLParameters;
28
+ }
@@ -0,0 +1,121 @@
1
+ import { UrlTileSetModel } from "@luciad/ria/model/tileset/UrlTileSetModel";
2
+ import { getReference } from "@luciad/ria/reference/ReferenceProvider";
3
+ import { createBounds } from "@luciad/ria/shape/ShapeFactory";
4
+ import { OgcOpenApiCrsTools } from "./OgcOpenApiCrsTools";
5
+ export class OgcOpenApiTilesModel extends UrlTileSetModel {
6
+ constructor(options) {
7
+ const crsName = OgcOpenApiCrsTools.getReferenceName(options.tileMatrix.crs);
8
+ const reference = getReference(crsName);
9
+ const levelCount = options.tileMatrix.tileMatrices.filter((t) => Number(t.id) > -1).length;
10
+ let invertY = true;
11
+ let axisX = 0;
12
+ let axisY = 1;
13
+ const orderedAxes0 = options.tileMatrix.orderedAxes[0].toUpperCase();
14
+ if (orderedAxes0 === "LAT" || orderedAxes0 === "N" || orderedAxes0 === "S") {
15
+ axisX = 1;
16
+ axisY = 0;
17
+ }
18
+ //const tm0 = options.tileMatrix.tileMatrices.find(t=>Number(t.id)===0);
19
+ const tm0 = options.tileMatrix.tileMatrices[0];
20
+ const calculated = {
21
+ tileWidth: 256,
22
+ tileHeight: 256,
23
+ };
24
+ let bounds;
25
+ if (options.tileMatrix.boundingBox && options.tileMatrix.boundingBox.lowerLeft && options.tileMatrix.boundingBox.upperRight) {
26
+ bounds = createBounds(reference, [
27
+ options.tileMatrix.boundingBox.lowerLeft[1], options.tileMatrix.boundingBox.upperRight[1] - options.tileMatrix.boundingBox.lowerLeft[1],
28
+ options.tileMatrix.boundingBox.lowerLeft[0], options.tileMatrix.boundingBox.upperRight[0] - options.tileMatrix.boundingBox.lowerLeft[0],
29
+ ]);
30
+ }
31
+ let level0Columns = 1;
32
+ let level0Rows = 1;
33
+ let tileHeight = 256;
34
+ let tileWidth = 256;
35
+ if (tm0) {
36
+ const cornerOfOrigin = tm0.cornerOfOrigin ? tm0.cornerOfOrigin : "TOPLEFT";
37
+ calculated.tileWidth = tm0.tileWidth;
38
+ calculated.tileHeight = tm0.tileHeight;
39
+ level0Rows = tm0.matrixHeight;
40
+ level0Columns = tm0.matrixWidth;
41
+ tileHeight = tm0.tileHeight;
42
+ tileWidth = tm0.tileHeight;
43
+ bounds = bounds ? bounds : reference.bounds;
44
+ if (cornerOfOrigin.toUpperCase() === "TOPLEFT") {
45
+ invertY = true;
46
+ // bounds = bounds ? bounds : createBounds(reference, [tm0.pointOfOrigin[axisX],tm0.matrixWidth, tm0.pointOfOrigin[axisY]-tm0.matrixHeight, tm0.matrixHeight])
47
+ }
48
+ else {
49
+ invertY = false;
50
+ //bounds = bounds ? bounds : createBounds(reference, [tm0.pointOfOrigin[axisX],tm0.matrixWidth, tm0.pointOfOrigin[axisY], tm0.matrixHeight])
51
+ }
52
+ }
53
+ const o = {
54
+ baseURL: options.baseURL,
55
+ reference: reference,
56
+ bounds: bounds,
57
+ tileHeight,
58
+ tileWidth,
59
+ levelCount: levelCount,
60
+ level0Columns,
61
+ level0Rows,
62
+ dataType: options.dataType,
63
+ samplingMode: options.samplingMode,
64
+ requestHeaders: options.requestHeaders ? options.requestHeaders : {},
65
+ };
66
+ super(o);
67
+ this.plevel0Rows = level0Rows;
68
+ this.transparent = typeof options.transparent !== "undefined" ? options.transparent : true;
69
+ this.bgcolor = options.bgcolor;
70
+ this.invertY = invertY;
71
+ this.tileMatrix = options.tileMatrix;
72
+ this.modelDescriptor = {
73
+ source: options.baseURL,
74
+ name: options.collection,
75
+ description: "OGC Open API Tiles",
76
+ type: super.dataType
77
+ };
78
+ }
79
+ getTileURL(baseURL, tile) {
80
+ const tileCorrected = Object.assign({}, tile);
81
+ // return this.debugAsImage(tileCorrected)
82
+ const maxY = this.plevel0Rows * Math.pow(2, tile.level) - 1;
83
+ tileCorrected.y = this.invertY ? maxY - tile.y : tile.y;
84
+ const zoomLevel = this.tileMatrix.tileMatrices.find(t => Number(t.id) === tile.level);
85
+ const level = zoomLevel ? zoomLevel.id : tile.level.toString();
86
+ const urlParameters = {
87
+ transparent: this.transparent,
88
+ bgcolor: this.transparent ? undefined : this.bgcolor
89
+ };
90
+ const query = OgcOpenApiTilesModel.createURLParameters(urlParameters);
91
+ const targetUrl = baseURL.replace("{tileMatrix}", level).replace("{tileRow}", tileCorrected.y.toString()).replace("{tileCol}", tileCorrected.x.toString());
92
+ return targetUrl + (query && query !== "") ? targetUrl + "?" + query : targetUrl;
93
+ }
94
+ debugAsImage(tileCorrected) {
95
+ const canvas = document.createElement('canvas');
96
+ if (canvas) {
97
+ canvas.width = this.getTileWidth(tileCorrected.level);
98
+ canvas.height = this.getTileHeight(tileCorrected.level);
99
+ const ctx = canvas.getContext("2d");
100
+ if (ctx) {
101
+ ctx.font = "30px Arial";
102
+ ctx.fillText(`x:${tileCorrected.x}, y:${tileCorrected.y}, z:${tileCorrected.level}`, 10, canvas.height / 2);
103
+ }
104
+ const dataURL = canvas.toDataURL();
105
+ return dataURL;
106
+ }
107
+ return null;
108
+ }
109
+ static createURLParameters(obj) {
110
+ let str = Object.keys(obj).filter(k => obj[k] !== undefined).map(function (key) {
111
+ if (Array.isArray(obj[key])) {
112
+ return key + '=' + obj[key].join(",");
113
+ }
114
+ if (typeof obj[key] == "boolean") {
115
+ return key + '=' + (obj[key] ? "true" : "false");
116
+ }
117
+ return key + '=' + obj[key];
118
+ }).join('&');
119
+ return str;
120
+ }
121
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ogcopenapis",
3
- "version": "0.0.1",
4
- "description": "",
3
+ "version": "0.0.3",
4
+ "description": "Implements OGC OPEN API Features/Maps/Tiles capabilities and models for LuciadRIA",
5
5
  "files": [
6
6
  "lib/"
7
7
  ],
@@ -23,7 +23,10 @@
23
23
  "@babel/preset-typescript": "^7.18.6",
24
24
  "@jest/globals": "^29.3.1",
25
25
  "copyfiles": "^2.3.0",
26
+ "isomorphic-fetch": "^3.0.0",
26
27
  "jest": "^29.3.1",
28
+ "jest-canvas-mock": "^2.4.0",
29
+ "jest-environment-jsdom": "^29.3.1",
27
30
  "typescript": "^4.6.3"
28
31
  },
29
32
  "dependencies": {