From 7b2fff71be7ad8b689dda5d06521bbc231e1ec5b Mon Sep 17 00:00:00 2001 From: Edward Nash Date: Tue, 24 Sep 2024 15:19:02 +0200 Subject: [PATCH 1/2] Expose provider information from capabilities It is often required as part of an application to show the metadata regarding the service provider which is included in capabilities documents (WFS 1.1+, WMS 1.1+, WMTS). This commit exposes this information in a common format as part of the GenericEndpointInfo structure. Note that this information is already included in the OgcApiCollectionInfo structure. --- src/shared/models.ts | 25 +++++++++++++++ src/shared/ows.spec.ts | 60 +++++++++++++++++++++++++++++++++++ src/shared/ows.ts | 52 ++++++++++++++++++++++++++++++ src/wfs/capabilities.spec.ts | 20 ++++++++++++ src/wfs/capabilities.ts | 7 ++++ src/wfs/endpoint.spec.ts | 18 +++++++++++ src/wms/capabilities.spec.ts | 17 ++++++++++ src/wms/capabilities.ts | 48 ++++++++++++++++++++++++++++ src/wms/endpoint.spec.ts | 17 ++++++++++ src/wmts/capabilities.spec.ts | 54 +++++++++++++++++++++++++++++++ src/wmts/capabilities.ts | 2 ++ src/wmts/endpoint.spec.ts | 36 +++++++++++++++++++++ 12 files changed, 356 insertions(+) create mode 100644 src/shared/ows.spec.ts create mode 100644 src/shared/ows.ts diff --git a/src/shared/models.ts b/src/shared/models.ts index c5eba38..d83637a 100644 --- a/src/shared/models.ts +++ b/src/shared/models.ts @@ -5,6 +5,30 @@ export type BoundingBox = [number, number, number, number]; export type CrsCode = string; +export interface Address { + deliveryPoint?: string; + city?: string; + administrativeArea?: string; + postalCode?: string; + country?: string; +} + +export interface Contact { + name?: string; + organization?: string; + position?: string; + phone?: string; + fax?: string; + address?: Address; + email?: string; +} + +export interface Provider { + name?: string; + site?: string; + contact?: Contact; +} + export type GenericEndpointInfo = { name: string; title: string; @@ -12,6 +36,7 @@ export type GenericEndpointInfo = { fees: string; constraints: string; keywords: string[]; + provider?: Provider; /** * Can contain the list of outputFormats from a WFS GetCapabilities, * or the list of 'Formats' from a WMS GetCapabilities diff --git a/src/shared/ows.spec.ts b/src/shared/ows.spec.ts new file mode 100644 index 0000000..71cbbfe --- /dev/null +++ b/src/shared/ows.spec.ts @@ -0,0 +1,60 @@ +// @ts-expect-error ts-migrate(7016) +import capabilitiesWfs110 from '../../fixtures/wfs/capabilities-pigma-1-1-0.xml'; +// @ts-expect-error ts-migrate(7016) +import capabilitiesWfs200 from '../../fixtures/wfs/capabilities-pigma-2-0-0.xml'; +// @ts-expect-error ts-migrate(7016) +import capabilitiesWmts from '../../fixtures/wmts/ogcsample.xml'; +import { parseXmlString } from './xml-utils.js'; +import { readProviderFromCapabilities } from './ows.js'; + +const wfsProvider = { + name: 'GIP ATGeRi', + site: '', + contact: { + name: 'PIGMA', + position: '', + phone: '05.57.85.40.42', + fax: '', + address: { + deliveryPoint: '', + city: 'Bordeaux', + administrativeArea: '', + postalCode: '33075', + country: '', + }, + email: 'admin.pigma@gipatgeri.fr', + }, +}; + +describe('OWS utils', () => { + it('can read provider information from WFS capabilities (2.0.0)', () => { + const doc = parseXmlString(capabilitiesWfs200); + expect(readProviderFromCapabilities(doc)).toEqual(wfsProvider); + }); + it('can read provider information from WFS capabilities (1.1.0)', () => { + const doc = parseXmlString(capabilitiesWfs110); + expect(readProviderFromCapabilities(doc)).toEqual(wfsProvider); + }); + it('can read provider information from WMTS capabilities', () => { + const expectedProvider = { + name: 'MiraMon', + site: 'http://www.creaf.uab.cat/miramon', + contact: { + name: 'Joan Maso Pau', + position: 'Senior Software Engineer', + phone: '+34 93 581 1312', + fax: '+34 93 581 4151', + address: { + deliveryPoint: 'Fac Ciencies UAB', + city: 'Bellaterra', + administrativeArea: 'Barcelona', + postalCode: '08193', + country: 'Spain', + }, + email: 'joan.maso@uab.cat', + }, + }; + const doc = parseXmlString(capabilitiesWmts); + expect(readProviderFromCapabilities(doc)).toEqual(expectedProvider); + }); +}); diff --git a/src/shared/ows.ts b/src/shared/ows.ts new file mode 100644 index 0000000..23940b3 --- /dev/null +++ b/src/shared/ows.ts @@ -0,0 +1,52 @@ +import type { XmlDocument } from '@rgrove/parse-xml'; +import type { Provider } from './models.js'; +import { + findChildElement, + getElementAttribute, + getElementText, + getRootElement, +} from './xml-utils.js'; + +/** + * Read standard OWS provider information from capabilities + * @param capabilitiesDoc + */ +export function readProviderFromCapabilities( + capabilitiesDoc: XmlDocument +): Provider { + const serviceProvider = findChildElement( + getRootElement(capabilitiesDoc), + 'ServiceProvider' + ); + const serviceContact = findChildElement(serviceProvider, 'ServiceContact'); + const contactInfo = findChildElement(serviceContact, 'ContactInfo'); + const phone = findChildElement(contactInfo, 'Phone'); + const address = findChildElement(contactInfo, 'Address'); + return { + name: getElementText(findChildElement(serviceProvider, 'ProviderName')), + site: getElementAttribute( + findChildElement(serviceProvider, 'ProviderSite'), + 'xlink:href' + ), + contact: { + name: getElementText(findChildElement(serviceContact, 'IndividualName')), + position: getElementText( + findChildElement(serviceContact, 'PositionName') + ), + phone: getElementText(findChildElement(phone, 'Voice')), + fax: getElementText(findChildElement(phone, 'Facsimile')), + address: { + deliveryPoint: getElementText( + findChildElement(address, 'DeliveryPoint') + ), + city: getElementText(findChildElement(address, 'City')), + administrativeArea: getElementText( + findChildElement(address, 'AdministrativeArea') + ), + postalCode: getElementText(findChildElement(address, 'PostalCode')), + country: getElementText(findChildElement(address, 'Country')), + }, + email: getElementText(findChildElement(address, 'ElectronicMailAddress')), + }, + }; +} diff --git a/src/wfs/capabilities.spec.ts b/src/wfs/capabilities.spec.ts index 67f50ab..11c12be 100644 --- a/src/wfs/capabilities.spec.ts +++ b/src/wfs/capabilities.spec.ts @@ -177,10 +177,29 @@ describe('WFS capabilities', () => { keywords: ['WFS', 'WMS', 'GEOSERVER'], outputFormats: [], }; + const provider = { + name: 'GIP ATGeRi', + site: '', + contact: { + name: 'PIGMA', + position: '', + phone: '05.57.85.40.42', + fax: '', + address: { + deliveryPoint: '', + city: 'Bordeaux', + administrativeArea: '', + postalCode: '33075', + country: '', + }, + email: 'admin.pigma@gipatgeri.fr', + }, + }; it('reads the service info (2.0.0)', () => { const doc = parseXmlString(capabilities200); expect(readInfoFromCapabilities(doc)).toEqual({ ...expectedInfo, + provider, outputFormats: [ 'application/gml+xml; version=3.2', 'DXF', @@ -208,6 +227,7 @@ describe('WFS capabilities', () => { const doc = parseXmlString(capabilities110); expect(readInfoFromCapabilities(doc)).toEqual({ ...expectedInfo, + provider, outputFormats: [ 'text/xml; subtype=gml/3.1.1', 'DXF', diff --git a/src/wfs/capabilities.ts b/src/wfs/capabilities.ts index ea7445a..8fce72a 100644 --- a/src/wfs/capabilities.ts +++ b/src/wfs/capabilities.ts @@ -1,3 +1,4 @@ +import { readProviderFromCapabilities } from '../shared/ows.js'; import { findChildElement, findChildrenElement, @@ -91,6 +92,11 @@ export function readInfoFromCapabilities( 'Keyword' ).map(getElementText); } + let provider; + // no provider information defined in capabilities for WFS 1.0.0 + if (version !== '1.0.0') { + provider = readProviderFromCapabilities(capabilitiesDoc); + } return { title: getElementText(findChildElement(service, 'Title')), @@ -99,6 +105,7 @@ export function readInfoFromCapabilities( fees: getElementText(findChildElement(service, 'Fees')), constraints: getElementText(findChildElement(service, 'AccessConstraints')), keywords, + provider, outputFormats: readOutputFormatsFromCapabilities(capabilitiesDoc), }; } diff --git a/src/wfs/endpoint.spec.ts b/src/wfs/endpoint.spec.ts index a6ebd86..ec4a622 100644 --- a/src/wfs/endpoint.spec.ts +++ b/src/wfs/endpoint.spec.ts @@ -226,6 +226,24 @@ describe('WfsEndpoint', () => { name: 'WFS', title: "Service WFS de l'IDS régionale PIGMA", keywords: ['WFS', 'WMS', 'GEOSERVER'], + provider: { + name: 'GIP ATGeRi', + site: '', + contact: { + name: 'PIGMA', + position: '', + phone: '05.57.85.40.42', + fax: '', + address: { + deliveryPoint: '', + city: 'Bordeaux', + administrativeArea: '', + postalCode: '33075', + country: '', + }, + email: 'admin.pigma@gipatgeri.fr', + }, + }, outputFormats: [ 'application/gml+xml; version=3.2', 'DXF', diff --git a/src/wms/capabilities.spec.ts b/src/wms/capabilities.spec.ts index 6fb4202..99c1e5b 100644 --- a/src/wms/capabilities.spec.ts +++ b/src/wms/capabilities.spec.ts @@ -306,6 +306,23 @@ describe('WMS capabilities', () => { 'WMS 1.3.0', 'SLD 1.1.0', ], + provider: { + contact: { + name: 'Support BRGM', + organization: 'BRGM', + position: 'pointOfContact', + phone: '+33(0)2 38 64 34 34', + fax: '+33(0)2 38 64 35 18', + address: { + deliveryPoint: '3, Avenue Claude Guillemin, BP36009', + city: 'Orléans', + administrativeArea: 'Centre', + postalCode: '45060', + country: 'France', + }, + email: 'contact-brgm@brgm.fr', + }, + }, }; it('reads the service info (1.3.0)', () => { diff --git a/src/wms/capabilities.ts b/src/wms/capabilities.ts index 71a9803..f596c2f 100644 --- a/src/wms/capabilities.ts +++ b/src/wms/capabilities.ts @@ -12,6 +12,7 @@ import { CrsCode, GenericEndpointInfo, LayerStyle, + type Provider, } from '../shared/models.js'; import { WmsLayerAttribution, WmsLayerFull, WmsVersion } from './model.js'; @@ -73,6 +74,7 @@ export function readInfoFromCapabilities( ) .map(getElementText) .filter((v, i, arr) => arr.indexOf(v) === i); + const provider = readProviderFromCapabilities(capabilitiesDoc); return { title: getElementText(findChildElement(service, 'Title')), @@ -81,6 +83,7 @@ export function readInfoFromCapabilities( outputFormats: formats, fees: getElementText(findChildElement(service, 'Fees')), constraints: getElementText(findChildElement(service, 'AccessConstraints')), + provider, keywords, }; } @@ -220,3 +223,48 @@ function parseLayerAttribution(attributionEl: XmlElement): WmsLayerAttribution { ...(logoUrl && { logoUrl }), }; } + +/** + * Read provider information from capabilities + * @param capabilitiesDoc + */ +function readProviderFromCapabilities(capabilitiesDoc: XmlDocument): Provider { + const service = findChildElement(getRootElement(capabilitiesDoc), 'Service'); + const contactInformation = findChildElement(service, 'ContactInformation'); + const contactPersonPrimary = findChildElement( + contactInformation, + 'ContactPersonPrimary' + ); + const address = findChildElement(contactInformation, 'ContactAddress'); + return { + contact: { + name: getElementText( + findChildElement(contactPersonPrimary, 'ContactPerson') + ), + organization: getElementText( + findChildElement(contactPersonPrimary, 'ContactOrganization') + ), + position: getElementText( + findChildElement(contactInformation, 'ContactPosition') + ), + phone: getElementText( + findChildElement(contactInformation, 'ContactVoiceTelephone') + ), + fax: getElementText( + findChildElement(contactInformation, 'ContactFacsimileTelephone') + ), + address: { + deliveryPoint: getElementText(findChildElement(address, 'Address')), + city: getElementText(findChildElement(address, 'City')), + administrativeArea: getElementText( + findChildElement(address, 'StateOrProvince') + ), + postalCode: getElementText(findChildElement(address, 'PostCode')), + country: getElementText(findChildElement(address, 'Country')), + }, + email: getElementText( + findChildElement(contactInformation, 'ContactElectronicMailAddress') + ), + }, + }; +} diff --git a/src/wms/endpoint.spec.ts b/src/wms/endpoint.spec.ts index c6bfdf4..aab31f9 100644 --- a/src/wms/endpoint.spec.ts +++ b/src/wms/endpoint.spec.ts @@ -196,6 +196,23 @@ describe('WmsEndpoint', () => { 'WMS 1.3.0', 'SLD 1.1.0', ], + provider: { + contact: { + name: 'Support BRGM', + organization: 'BRGM', + position: 'pointOfContact', + phone: '+33(0)2 38 64 34 34', + fax: '+33(0)2 38 64 35 18', + address: { + deliveryPoint: '3, Avenue Claude Guillemin, BP36009', + city: 'Orléans', + administrativeArea: 'Centre', + postalCode: '45060', + country: 'France', + }, + email: 'contact-brgm@brgm.fr', + }, + }, }); }); }); diff --git a/src/wmts/capabilities.spec.ts b/src/wmts/capabilities.spec.ts index 4c8d97a..c6a4f0b 100644 --- a/src/wmts/capabilities.spec.ts +++ b/src/wmts/capabilities.spec.ts @@ -23,6 +23,24 @@ describe('WMTS Capabilities', () => { fees: 'none', keywords: ['tile', 'tile matrix set', 'map'], name: 'OGC WMTS', + provider: { + name: 'MiraMon', + site: 'http://www.creaf.uab.cat/miramon', + contact: { + name: 'Joan Maso Pau', + position: 'Senior Software Engineer', + phone: '+34 93 581 1312', + fax: '+34 93 581 4151', + address: { + deliveryPoint: 'Fac Ciencies UAB', + city: 'Bellaterra', + administrativeArea: 'Barcelona', + postalCode: '08193', + country: 'Spain', + }, + email: 'joan.maso@uab.cat', + }, + }, title: 'Web Map Tile Service', getTileUrls: { kvp: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', @@ -39,6 +57,24 @@ describe('WMTS Capabilities', () => { fees: '', keywords: [], name: 'OGC WMTS', + provider: { + name: '', + site: '', + contact: { + name: '', + position: '', + phone: '', + fax: '', + address: { + deliveryPoint: '', + city: '', + administrativeArea: '', + postalCode: '', + country: '', + }, + email: '', + }, + }, title: 'Demographics_USA_Population_Density', getTileUrls: { rest: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/', @@ -76,6 +112,24 @@ describe('WMTS Capabilities', () => { 'Altitude', ], name: 'OGC WMTS', + provider: { + name: 'IGN', + site: '', + contact: { + name: 'Géoportail SAV', + position: 'custodian', + phone: '', + fax: '', + address: { + deliveryPoint: '73 avenue de Paris', + city: 'Saint Mandé', + administrativeArea: '', + postalCode: '94160', + country: 'France', + }, + email: 'geop_services@geoportail.fr', + }, + }, title: 'Service de visualisation WMTS', getTileUrls: { kvp: 'http://wxs.ign.fr/geoportail/wmts?', diff --git a/src/wmts/capabilities.ts b/src/wmts/capabilities.ts index ab33b7b..e00ba91 100644 --- a/src/wmts/capabilities.ts +++ b/src/wmts/capabilities.ts @@ -1,4 +1,5 @@ import type { BoundingBox, LayerStyle } from '../shared/models.js'; +import { readProviderFromCapabilities } from '../shared/ows.js'; import { findChildElement, findChildrenElement, @@ -59,6 +60,7 @@ export function readInfoFromCapabilities( fees: getElementText(findChildElement(service, 'Fees')), constraints: getElementText(findChildElement(service, 'AccessConstraints')), keywords, + provider: readProviderFromCapabilities(capabilitiesDoc), getTileUrls, }; } diff --git a/src/wmts/endpoint.spec.ts b/src/wmts/endpoint.spec.ts index d058be4..f0817b5 100644 --- a/src/wmts/endpoint.spec.ts +++ b/src/wmts/endpoint.spec.ts @@ -240,6 +240,24 @@ describe('WmtsEndpoint', () => { fees: 'none', keywords: ['tile', 'tile matrix set', 'map'], name: 'OGC WMTS', + provider: { + name: 'MiraMon', + site: 'http://www.creaf.uab.cat/miramon', + contact: { + name: 'Joan Maso Pau', + position: 'Senior Software Engineer', + phone: '+34 93 581 1312', + fax: '+34 93 581 4151', + address: { + deliveryPoint: 'Fac Ciencies UAB', + city: 'Bellaterra', + administrativeArea: 'Barcelona', + postalCode: '08193', + country: 'Spain', + }, + email: 'joan.maso@uab.cat', + }, + }, title: 'Web Map Tile Service', getTileUrls: { kvp: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', @@ -345,6 +363,24 @@ describe('WmtsEndpoint', () => { keywords: [], name: 'OGC WMTS', title: 'Demographics_USA_Population_Density', + provider: { + name: '', + site: '', + contact: { + name: '', + position: '', + phone: '', + fax: '', + address: { + deliveryPoint: '', + city: '', + administrativeArea: '', + postalCode: '', + country: '', + }, + email: '', + }, + }, }); }); }); From 7806d2dbf142c09d2f1ffbaed209f87ee28bfe3e Mon Sep 17 00:00:00 2001 From: Edward Nash Date: Wed, 25 Sep 2024 17:39:57 +0200 Subject: [PATCH 2/2] Add newly defined types to public exports --- src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.ts b/src/index.ts index 6f6b259..2fcce9a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,9 @@ export type { WmtsMatrixSet, } from './wmts/model.js'; export type { + Address, + Contact, + Provider, LayerStyle, BoundingBox, FetchOptions,