ogcopenapis 0.0.1 → 0.0.2
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 +1 -1
- package/lib/OgcOpenApiCrsTools.d.ts +9 -0
- package/lib/OgcOpenApiCrsTools.js +32 -0
- package/lib/OgcOpenApiFeatureStore.d.ts +86 -0
- package/lib/OgcOpenApiFeatureStore.js +206 -0
- package/lib/OgcOpenApiFetchTools.d.ts +31 -0
- package/lib/OgcOpenApiFetchTools.js +46 -0
- package/lib/OgcOpenApiGetCapabilities.d.ts +114 -0
- package/lib/OgcOpenApiGetCapabilities.js +276 -0
- package/lib/OgcOpenApiGetCollectionTiles.d.ts +7 -0
- package/lib/OgcOpenApiGetCollectionTiles.js +7 -0
- package/lib/OgcOpenApiMapsModel.d.ts +33 -0
- package/lib/OgcOpenApiMapsModel.js +70 -0
- package/lib/OgcOpenApiTilesModel.d.ts +28 -0
- package/lib/OgcOpenApiTilesModel.js +122 -0
- package/package.json +4 -1
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
|
-
|
|
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,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,33 @@
|
|
|
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
|
+
static REVERSED_PPROJECTIONS: string[];
|
|
23
|
+
private format;
|
|
24
|
+
private crs;
|
|
25
|
+
private reverseAxis;
|
|
26
|
+
private datetime;
|
|
27
|
+
private subset;
|
|
28
|
+
private bgcolor;
|
|
29
|
+
constructor(o: OgcOpenApiMapsModelConstructorOptions);
|
|
30
|
+
getTileURL(baseURL: string, tile: TileCoordinate): string | null;
|
|
31
|
+
private static createURLParameters;
|
|
32
|
+
}
|
|
33
|
+
export { OgcOpenApiMapsModel };
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
OgcOpenApiMapsModel.REVERSED_PPROJECTIONS = ["EPSG:4269"];
|
|
70
|
+
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,122 @@
|
|
|
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
|
+
console.log(reference.axisInformation);
|
|
19
|
+
//const tm0 = options.tileMatrix.tileMatrices.find(t=>Number(t.id)===0);
|
|
20
|
+
const tm0 = options.tileMatrix.tileMatrices[0];
|
|
21
|
+
const calculated = {
|
|
22
|
+
tileWidth: 256,
|
|
23
|
+
tileHeight: 256,
|
|
24
|
+
};
|
|
25
|
+
let bounds;
|
|
26
|
+
if (options.tileMatrix.boundingBox && options.tileMatrix.boundingBox.lowerLeft && options.tileMatrix.boundingBox.upperRight) {
|
|
27
|
+
bounds = createBounds(reference, [
|
|
28
|
+
options.tileMatrix.boundingBox.lowerLeft[1], options.tileMatrix.boundingBox.upperRight[1] - options.tileMatrix.boundingBox.lowerLeft[1],
|
|
29
|
+
options.tileMatrix.boundingBox.lowerLeft[0], options.tileMatrix.boundingBox.upperRight[0] - options.tileMatrix.boundingBox.lowerLeft[0],
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
let level0Columns = 1;
|
|
33
|
+
let level0Rows = 1;
|
|
34
|
+
let tileHeight = 256;
|
|
35
|
+
let tileWidth = 256;
|
|
36
|
+
if (tm0) {
|
|
37
|
+
const cornerOfOrigin = tm0.cornerOfOrigin ? tm0.cornerOfOrigin : "TOPLEFT";
|
|
38
|
+
calculated.tileWidth = tm0.tileWidth;
|
|
39
|
+
calculated.tileHeight = tm0.tileHeight;
|
|
40
|
+
level0Rows = tm0.matrixHeight;
|
|
41
|
+
level0Columns = tm0.matrixWidth;
|
|
42
|
+
tileHeight = tm0.tileHeight;
|
|
43
|
+
tileWidth = tm0.tileHeight;
|
|
44
|
+
bounds = bounds ? bounds : reference.bounds;
|
|
45
|
+
if (cornerOfOrigin.toUpperCase() === "TOPLEFT") {
|
|
46
|
+
invertY = true;
|
|
47
|
+
// bounds = bounds ? bounds : createBounds(reference, [tm0.pointOfOrigin[axisX],tm0.matrixWidth, tm0.pointOfOrigin[axisY]-tm0.matrixHeight, tm0.matrixHeight])
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
invertY = false;
|
|
51
|
+
//bounds = bounds ? bounds : createBounds(reference, [tm0.pointOfOrigin[axisX],tm0.matrixWidth, tm0.pointOfOrigin[axisY], tm0.matrixHeight])
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const o = {
|
|
55
|
+
baseURL: options.baseURL,
|
|
56
|
+
reference: reference,
|
|
57
|
+
bounds: bounds,
|
|
58
|
+
tileHeight,
|
|
59
|
+
tileWidth,
|
|
60
|
+
levelCount: levelCount,
|
|
61
|
+
level0Columns,
|
|
62
|
+
level0Rows,
|
|
63
|
+
dataType: options.dataType,
|
|
64
|
+
samplingMode: options.samplingMode,
|
|
65
|
+
requestHeaders: options.requestHeaders ? options.requestHeaders : {},
|
|
66
|
+
};
|
|
67
|
+
super(o);
|
|
68
|
+
this.plevel0Rows = level0Rows;
|
|
69
|
+
this.transparent = typeof options.transparent !== "undefined" ? options.transparent : true;
|
|
70
|
+
this.bgcolor = options.bgcolor;
|
|
71
|
+
this.invertY = invertY;
|
|
72
|
+
this.tileMatrix = options.tileMatrix;
|
|
73
|
+
this.modelDescriptor = {
|
|
74
|
+
source: options.baseURL,
|
|
75
|
+
name: options.collection,
|
|
76
|
+
description: "OGC Open API Tiles",
|
|
77
|
+
type: super.dataType
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
getTileURL(baseURL, tile) {
|
|
81
|
+
const tileCorrected = Object.assign({}, tile);
|
|
82
|
+
// return this.debugAsImage(tileCorrected)
|
|
83
|
+
const maxY = this.plevel0Rows * Math.pow(2, tile.level) - 1;
|
|
84
|
+
tileCorrected.y = this.invertY ? maxY - tile.y : tile.y;
|
|
85
|
+
const zoomLevel = this.tileMatrix.tileMatrices.find(t => Number(t.id) === tile.level);
|
|
86
|
+
const level = zoomLevel ? zoomLevel.id : tile.level.toString();
|
|
87
|
+
const urlParameters = {
|
|
88
|
+
transparent: this.transparent,
|
|
89
|
+
bgcolor: this.transparent ? undefined : this.bgcolor
|
|
90
|
+
};
|
|
91
|
+
const query = OgcOpenApiTilesModel.createURLParameters(urlParameters);
|
|
92
|
+
const targetUrl = baseURL.replace("{tileMatrix}", level).replace("{tileRow}", tileCorrected.y.toString()).replace("{tileCol}", tileCorrected.x.toString());
|
|
93
|
+
return targetUrl + (query && query !== "") ? targetUrl + "?" + query : targetUrl;
|
|
94
|
+
}
|
|
95
|
+
debugAsImage(tileCorrected) {
|
|
96
|
+
const canvas = document.createElement('canvas');
|
|
97
|
+
if (canvas) {
|
|
98
|
+
canvas.width = this.getTileWidth(tileCorrected.level);
|
|
99
|
+
canvas.height = this.getTileHeight(tileCorrected.level);
|
|
100
|
+
const ctx = canvas.getContext("2d");
|
|
101
|
+
if (ctx) {
|
|
102
|
+
ctx.font = "30px Arial";
|
|
103
|
+
ctx.fillText(`x:${tileCorrected.x}, y:${tileCorrected.y}, z:${tileCorrected.level}`, 10, canvas.height / 2);
|
|
104
|
+
}
|
|
105
|
+
const dataURL = canvas.toDataURL();
|
|
106
|
+
return dataURL;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
static createURLParameters(obj) {
|
|
111
|
+
let str = Object.keys(obj).filter(k => obj[k] !== undefined).map(function (key) {
|
|
112
|
+
if (Array.isArray(obj[key])) {
|
|
113
|
+
return key + '=' + obj[key].join(",");
|
|
114
|
+
}
|
|
115
|
+
if (typeof obj[key] == "boolean") {
|
|
116
|
+
return key + '=' + (obj[key] ? "true" : "false");
|
|
117
|
+
}
|
|
118
|
+
return key + '=' + obj[key];
|
|
119
|
+
}).join('&');
|
|
120
|
+
return str;
|
|
121
|
+
}
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ogcopenapis",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib/"
|
|
@@ -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": {
|