@loaders.gl/wms
Version:
Framework-independent loaders for the WMS (Web Map Service) standard
183 lines • 7.12 kB
JavaScript
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { DataSource } from '@loaders.gl/loader-utils';
import { CSWCapabilitiesLoader } from "./csw-capabilities-loader.js";
import { CSWRecordsLoader } from "./csw-records-loader.js";
import { CSWDomainLoader } from "./csw-domain-loader.js";
import { WMSErrorLoader as CSWErrorLoader } from "./wms-error-loader.js";
export const CSWSource = {
name: 'CSW',
id: 'csw',
module: 'wms',
version: '0.0.0',
extensions: [],
mimeTypes: [],
type: 'csw',
fromUrl: true,
fromBlob: false,
defaultOptions: {
wfs: {}
},
testURL: (url) => url.toLowerCase().includes('wfs'),
createDataSource: (url, options) => new CSWCatalogSource(url, options)
};
/**
* The CSWCatalogSource class
* - provides type safe methods to form URLs to a CSW service
* - provides type safe methods to query and parse results (and errors) from a CSW service
* @note Only the URL parameter conversion is supported. XML posts are not supported.
*/
export class CSWCatalogSource extends DataSource {
static type = 'csw';
static testURL = (url) => url.toLowerCase().includes('csw');
capabilities = null;
/** A list of loaders used by the CSWCatalogSource methods */
loaders = [CSWErrorLoader, CSWCapabilitiesLoader];
/** Create a CSWCatalogSource */
constructor(url, options) {
super(url, options, CSWSource.defaultOptions);
}
async getMetadata() {
const capabilities = await this.getCapabilities();
return this.normalizeMetadata(capabilities);
}
normalizeMetadata(capabilities) {
return capabilities;
}
async getServiceDirectory(options) {
const services = [];
const unknownServices = [];
const records = await this.getRecords();
for (const record of records.records) {
for (const reference of record.references) {
const url = reference.value;
switch (reference.scheme) {
case 'OGC:WMS':
services.push({ name: record.title, type: 'ogc-wms-service', ...this._parseOGCUrl(url) });
break;
case 'OGC:WMTS':
services.push({
name: record.title,
type: 'ogc-wmts-service',
...this._parseOGCUrl(url)
});
break;
case 'OGC:WFS':
services.push({ name: record.title, type: 'ogc-wfs-service', ...this._parseOGCUrl(url) });
break;
default:
unknownServices.push({
name: record.title,
type: 'unknown',
url: reference.value,
scheme: reference.scheme
});
}
}
}
return options?.includeUnknown ? services.concat(unknownServices) : services;
}
_parseOGCUrl(url) {
const parts = url.split('?');
return {
url: parts[0],
params: parts[1] || ''
};
}
// CSW Service API Stubs
/** Get Capabilities */
async getCapabilities(cswParameters, vendorParameters) {
const url = this.getCapabilitiesURL(cswParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
const capabilities = await CSWCapabilitiesLoader.parse(arrayBuffer, this.options.core.loadOptions);
return capabilities;
}
/** Get Records */
async getRecords(cswParameters, vendorParameters) {
const url = this.getRecordsURL(cswParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
return await CSWRecordsLoader.parse(arrayBuffer, this.options.core.loadOptions);
}
/** Get Domain */
async getDomain(cswParameters, vendorParameters) {
const url = this.getDomainURL(cswParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
return await CSWDomainLoader.parse(arrayBuffer, this.options.core.loadOptions);
}
// Typed URL creators
// For applications that want full control of fetching and parsing
/** Generate a URL for the GetCapabilities request */
getCapabilitiesURL(cswParameters, vendorParameters) {
const options = {
version: '3.0.0',
...cswParameters,
...vendorParameters,
service: 'CSW',
request: 'GetCapabilities'
};
return this._getCSWUrl(options, vendorParameters);
}
/** Generate a URL for the GetCapabilities request */
getRecordsURL(cswParameters, vendorParameters) {
const options = {
version: '3.0.0',
typenames: 'csw:Record',
...cswParameters,
...vendorParameters,
service: 'CSW',
request: 'GetRecords'
};
return this._getCSWUrl(options, vendorParameters);
}
/** Generate a URL for the GetCapabilities request */
getDomainURL(cswParameters, vendorParameters) {
const options = {
version: '3.0.0',
...cswParameters,
...vendorParameters,
service: 'CSW',
request: 'GetDomain'
};
return this._getCSWUrl(options, vendorParameters);
}
// INTERNAL METHODS
/**
* @note case _getCSWUrl may need to be overridden to handle certain backends?
* */
_getCSWUrl(options, vendorParameters) {
let url = this.url;
let first = true;
for (const [key, value] of Object.entries(options)) {
url += first ? '?' : '&';
first = false;
if (Array.isArray(value)) {
url += `${key.toUpperCase()}=${value.join(',')}`;
}
else {
url += `${key.toUpperCase()}=${value ? String(value) : ''}`;
}
}
return encodeURI(url);
}
/** Checks for and parses a CSW XML formatted ServiceError and throws an exception */
_checkResponse(response, arrayBuffer) {
const contentType = response.headers['content-type'];
if (!response.ok || CSWErrorLoader.mimeTypes.includes(contentType)) {
const error = CSWErrorLoader.parseSync?.(arrayBuffer, this.options.core.loadOptions);
throw new Error(error);
}
}
/** Error situation detected */
_parseError(arrayBuffer) {
const error = CSWErrorLoader.parseSync?.(arrayBuffer, this.options.core.loadOptions);
return new Error(error);
}
}
//# sourceMappingURL=csw-source.js.map