@loaders.gl/wms
Version:
Framework-independent loaders for the WMS (Web Map Service) standard
171 lines (170 loc) • 6.71 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";
/**
* The CSWService 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 CSWService extends DataSource {
static type = 'csw';
static testURL = (url) => url.toLowerCase().includes('csw');
capabilities = null;
data;
url;
/** A list of loaders used by the CSWService methods */
loaders = [CSWErrorLoader, CSWCapabilitiesLoader];
/** Create a CSWService */
constructor(props) {
super(props);
this.url = props.url;
this.data = props.url;
}
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(wmsParameters, vendorParameters) {
const url = this.getCapabilitiesURL(wmsParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
const capabilities = await CSWCapabilitiesLoader.parse(arrayBuffer, this.props.loadOptions);
return capabilities;
}
/** Get Records */
async getRecords(wmsParameters, vendorParameters) {
const url = this.getRecordsURL(wmsParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
return await CSWRecordsLoader.parse(arrayBuffer, this.props.loadOptions);
}
/** Get Domain */
async getDomain(wmsParameters, vendorParameters) {
const url = this.getDomainURL(wmsParameters, vendorParameters);
const response = await this.fetch(url);
const arrayBuffer = await response.arrayBuffer();
this._checkResponse(response, arrayBuffer);
return await CSWDomainLoader.parse(arrayBuffer, this.props.loadOptions);
}
// Typed URL creators
// For applications that want full control of fetching and parsing
/** Generate a URL for the GetCapabilities request */
getCapabilitiesURL(wmsParameters, vendorParameters) {
const options = {
version: '3.0.0',
...wmsParameters,
...vendorParameters,
service: 'CSW',
request: 'GetCapabilities'
};
return this._getCSWUrl(options, vendorParameters);
}
/** Generate a URL for the GetCapabilities request */
getRecordsURL(wmsParameters, vendorParameters) {
const options = {
version: '3.0.0',
typenames: 'csw:Record',
...wmsParameters,
...vendorParameters,
service: 'CSW',
request: 'GetRecords'
};
return this._getCSWUrl(options, vendorParameters);
}
/** Generate a URL for the GetCapabilities request */
getDomainURL(wmsParameters, vendorParameters) {
const options = {
version: '3.0.0',
...wmsParameters,
...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.props.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.props.loadOptions);
throw new Error(error);
}
}
/** Error situation detected */
_parseError(arrayBuffer) {
const error = CSWErrorLoader.parseSync?.(arrayBuffer, this.props.loadOptions);
return new Error(error);
}
}