UNPKG

@dlr-eoc/services-ogc

Version:

This module bundles our clients for OGC standards. E.g. parse OWS Context JSON, WMS, WMTS or WPS.

1 lines 159 kB
{"version":3,"file":"dlr-eoc-services-ogc.mjs","sources":["../../../projects/services-ogc/src/lib/owc/types/owc-json.ts","../../../projects/services-ogc/src/lib/owc/types/eoc-owc-json.ts","../../../projects/services-ogc/src/lib/owc/types/owc-json.utils.ts","../../../projects/services-ogc/src/lib/wmts/wmtsclient.service.ts","../../../projects/services-ogc/src/lib/owc/owc-json.service.ts","../../../projects/services-ogc/src/lib/wps/wpsclient.ts","../../../projects/services-ogc/src/lib/wms/wmsclient.service.ts","../../../projects/services-ogc/src/public-api.ts","../../../projects/services-ogc/src/dlr-eoc-services-ogc.ts"],"sourcesContent":["/**\r\n * Type definitions for OGC OWS Context Geo Encoding Standard Version: 1.0\r\n * http://docs.opengeospatial.org/is/14-055r2/14-055r2.html\r\n * Definitions by: Mathias Boeck\r\n * TypeScript Version: 2.5.3\r\n *\r\n * depends on @types/geojson@^7946.0.2\r\n */\r\nimport * as GeoJSON from 'geojson';\r\n\r\n/**\r\n * The OWS Context describes Metadata, API, Time Range\r\n * http://www.owscontext.org/owc_user_guide/C0_userGuide.html#truethe-ows-context-document-structure\r\n * If no bounding box is specified, do not change the current view when the context document is loaded.\r\n */\r\nexport interface IOwsContext extends GeoJSON.FeatureCollection<GeoJSON.GeometryObject | null, GeoJSON.GeoJsonProperties> {\r\n /**\r\n * The id element defines a mandatory reference to the identification of the Context document.\r\n * The content for the id element SHALL be an IRI, as defined by IETF [RFC3987]\r\n */\r\n id: string | number;\r\n properties: {\r\n links: {\r\n profiles: IOwsLinks[],\r\n via?: IOwsLinks[]\r\n };\r\n /** Language of Context document content */\r\n lang: LangString;\r\n /** Title for the Context document */\r\n title: string;\r\n /** Date of a creation or update of the Context document */\r\n updated: DateString;\r\n /** Description of the Context document purpose or content */\r\n subtitle?: string;\r\n /** This element is optional and indicates the authors array of the Context document */\r\n authors?: IOwsAuthor[];\r\n /** Identifier for the publisher of the Context document */\r\n publisher?: string;\r\n /** Tool/application used to create the Context document and its properties */\r\n generator?: IOwsGenerator;\r\n /**\r\n * Properties of the display in use when the context document was created (for display based applications only).\r\n * This class is optional and intended for creator applications that use a graphical user interface with a geographical display within a fixed pixel size and not scalable to different computational devices\r\n */\r\n display?: IOwsCreatorDisplay[];\r\n /** Information about rights held in and over the Context document */\r\n rights?: string;\r\n /**\r\n * This element is optional and expressed a date or range of dates relevant to the Context document.\r\n * It can contain the element start, stop and instant. The values of these elements SHALL conform to the \"date-time\" production of ISO-8601[5]. An uppercase \"T\" character SHALL be used to separate date and time, and an uppercase \"Z\" character SHALL be present in the absence of a numeric time zone offset. To specify a range of dates the \"/\" character SHALL be used.\r\n */\r\n date?: DateString;\r\n /** This array is an optional and expresses categories related to this Context document */\r\n categories?: IOwsCategory[];\r\n /** Extension Any other element */\r\n [k: string]: any;\r\n };\r\n /** Ordered List of Resources available on the Context document\r\n * The order of the member of the features MAY be used to identify the drawing order of the resources.\r\n * In that case, the first item of the array represents the top most layer\r\n */\r\n features: IOwsResource[];\r\n /** Extension Any other element */\r\n [k: string]: any;\r\n}\r\n\r\n/**\r\n * Each layer (a.k.a. feature) in a context document is known as a ‘Resource’\r\n * A Resource reference a set of geospatial information to be treated as a logical element.\r\n * The resources are ordered such that the first item in the document is to be displayed at the front.\r\n * This defines the order in which layers are drawn.\r\n * A resource (which in GIS terms is a layer) can have a number of offerings, and each offering\r\n * is focussed on a particular representation of information.\r\n * These can be one of a number of OGC Web Services, specifically WMS, WMTS, WFS, WCS, WPS and CSW,\r\n * or one of a number of inline or referenced formats, specifically GML, KML, GeoTIFF, GMLJP2, GMLCOV,\r\n * or a custom offering type defined in a profile or by an organisation.\r\n * http://www.owscontext.org/owc_user_guide/C0_userGuide.html#truethe-ows-context-document-structure\r\n */\r\nexport interface IOwsResource extends GeoJSON.Feature {\r\n /**\r\n * Unambiguous reference to the identification of the Context resource (IRI)\r\n * String type that SHALL contain a URI value\r\n */\r\n id: string | number;\r\n properties: IOwsResourceProperties;\r\n [k: string]: any;\r\n}\r\nexport interface IOwsResourceProperties {\r\n /** Title given to the Context resource */\r\n title: string;\r\n /** Date of the last update of the Context resource */\r\n updated: DateString;\r\n /** The purpose is to provide a generic description of the content in a format understandable by generic readers */\r\n abstract?: string;\r\n /** This element is optional and indicates the authors array of the Context resource */\r\n authors?: IOwsAuthor[];\r\n /** Entity responsible for making the Context resource available */\r\n publisher?: string;\r\n /** Information about rights held in and over the Context resource */\r\n rights?: string;\r\n /** Date or range of dates relevant to the Context resource. The values of these elements SHALL conform to the \"date-time\" production of ISO-8601[5]*/\r\n date?: DateString;\r\n /** This element is optional and can contain a number of offerings defined by the class OWC:Offering */\r\n offerings?: IOwsOffering[];\r\n /** Flag value indicating to the client if the Context resource should be displayed by default.\r\n * E.g. Layer is visible\r\n */\r\n active?: boolean;\r\n /** This array is optional and expresses a category related to the Context resource */\r\n categories?: IOwsCategory[];\r\n /** Minimum scale for the display of the Context resource Double */\r\n minscaledenominator?: number;\r\n /** Maximum scale for the display of the Context resource Double */\r\n maxscaledenominator?: number;\r\n /** Definition of the folder in which the resource is placed\r\n * The folder attribute is intended to support the concept present in many clients or organising layers into folders.\r\n */\r\n folder?: string;\r\n links?: {\r\n previews?: IOwsLinks[],\r\n alternates?: IOwsLinks[],\r\n data?: IOwsLinks[],\r\n via?: IOwsLinks[]\r\n };\r\n [k: string]: any;\r\n}\r\n\r\n/**\r\n * In reality a resource can be realized in a number of different ways, and so an OWC document allows various options to be specified.\r\n * These are known as offerings.\r\n * The intention is that these are, as far as is possible by the format used,\r\n * equivalent and no priority is assigned to their order in the standard.\r\n * They are intended to be alternatives that the client can use to allow it to visualize or use the resource.\r\n *\r\n * So for example four offerings, a WMS, a WFS with portrayal as SLD, and an inline GML Offering again with portrayal as SLD.\r\n * Different clients could use these offerings as appropriate:\r\n * - a simple browser based client could use the WMS offering provided, using the standard portrayal\r\n * - a more sophisticated client, could use the WFS offering and the associated SLD Document.\r\n *\r\n * There are two types of offering, service offerings and data offerings.\r\n * A service offering has a service request (in the form of a capabilities request and a data request)\r\n * and optional content and styling elements.\r\n * A data offering has a content element and optional styling elements.\r\n *\r\n *\r\n * http://www.owscontext.org/owc_user_guide/C0_userGuide.html#truemultiple-offerings-and-priority\r\n */\r\nexport interface IOwsOffering {\r\n /** Code identifying the type of offering - Extension Offerings with type - string */\r\n code: WMS_Offering | WFS_Offering | WCS_Offering | WPS_Offering | CSW_Offering | WMTS_Offering |\r\n GML_Offering | KML_Offering | GeoTIFF_Offering | GMLJP2_Offering | GMLCOV_Offering | string;\r\n /** Web Service Offerings provide their operations - Array of operations used to invoke the service */\r\n operations?: IOwsOperation[];\r\n /** Content Offerings allow content to be embedded in an OWS Context document (inline or byRef) */\r\n contents?: IOwsContent[];\r\n /** Array of style sets - A style representation for a resource (inline or service derived) content */\r\n styles?: IOwsStyleSet[];\r\n [k: string]: any;\r\n}\r\n\r\nexport interface IOwsGenerator {\r\n title?: string;\r\n uri?: string;\r\n version?: string;\r\n}\r\n\r\nexport interface IOwsAuthor {\r\n /** Entity primarily responsible for making the Context document\r\n * Properties that all types of authors have. It mimics the Atom author\r\n */\r\n name: string;\r\n email?: string;\r\n uri?: string;\r\n [k: string]: any;\r\n}\r\n\r\nexport interface IOwsCategory {\r\n /** Category related to this context document. It MAY have a related code-list that is identified by the scheme attribute */\r\n term: string;\r\n scheme?: string;\r\n label?: string;\r\n}\r\n\r\n/** Properties that all types of links have. It mimics the Atom link */\r\nexport interface IOwsLinks {\r\n href: string;\r\n type?: string;\r\n title?: string;\r\n lang?: LangString;\r\n length?: number;\r\n [k: string]: any;\r\n}\r\n\r\nexport interface IOwsCreatorApplication {\r\n title?: string;\r\n uri?: string;\r\n version?: string;\r\n}\r\n\r\nexport interface IOwsCreatorDisplay {\r\n /** Width measured in pixels of the display showing the Area of Interest */\r\n pixelWidth?: number;\r\n /** Width measured in pixels of the display showing by the Area of Interest */\r\n pixelHeight?: number;\r\n /** The size of a pixel of the display in millimeters\r\n * (combined with the previous ones allows for the real display size to be calculated)\r\n */\r\n mmPerPixel?: number;\r\n [k: string]: any;\r\n}\r\n\r\n/**\r\n * Most service offerings have two operations, a ‘GetCapabilities’ operation and a data operation such as ‘GetMap’ for WMS\r\n */\r\nexport interface IOwsOperation {\r\n /**\r\n * The code identifies the type of operation.\r\n * Valid types are defined within each specific extension within the OWS Context conceptual model [OGC 12-080].\r\n */\r\n code: string;\r\n /** method defines the access method, for example GET or POST. */\r\n method: string;\r\n /** Service Request URL - The URI containing the definition of the request */\r\n href: string;\r\n /** MIME type of the expected results */\r\n type?: string;\r\n /** Optional request body content */\r\n request?: IOwsContent;\r\n /** Optional Result Payload of the operation */\r\n result?: IOwsContent;\r\n /** Extension of Operation */\r\n [k: string]: any;\r\n}\r\n\r\nexport interface IOwsContent {\r\n /** MIME type of the Content */\r\n type: string;\r\n /** URL of the Content */\r\n href?: string;\r\n /** Title of the Content */\r\n title?: string;\r\n /** In-line content for the Content element- String type, not empty that can contain any text encoded media type */\r\n content?: string;\r\n [k: string]: any;\r\n}\r\n\r\nexport interface IOwsStyleSet {\r\n /** Unique name of the styleSet within a given offering */\r\n name: string;\r\n /** Human Readable title of the styleSet within a given offering */\r\n title: string;\r\n /** Description of the styleSet */\r\n abstract?: string;\r\n /** Whether this styleSet is the one to be defined by default */\r\n default?: boolean;\r\n /** URL of a legend image for the styleSet */\r\n legendURL?: string;\r\n /** The inline or a external reference to the styleSet definition */\r\n content?: IOwsContent;\r\n [k: string]: any;\r\n}\r\n\r\n/** ISO-8601 format e.g. YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ssZ/YYYY-MM-DDThh:mm:ssZ */\r\nexport type DateString = string;\r\n\r\n\r\n/** RFC-3066 code e.g. en,de */\r\nexport type LangString = string;\r\n\r\nexport const wmsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/wms' as const;\r\nexport type WMS_Offering = typeof wmsOffering;\r\n\r\nexport const wfsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/wfs' as const;\r\nexport type WFS_Offering = typeof wfsOffering;\r\n\r\nexport const wcsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/wcs' as const;\r\nexport type WCS_Offering = typeof wcsOffering;\r\n\r\nexport const wpsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/wps' as const;\r\nexport type WPS_Offering = typeof wpsOffering;\r\n\r\nexport const cswOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/csw' as const;\r\nexport type CSW_Offering = typeof cswOffering;\r\n\r\nexport const wmtsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/wmts' as const;\r\nexport type WMTS_Offering = typeof wmtsOffering;\r\n\r\nexport const gmlOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/gml' as const;\r\nexport type GML_Offering = typeof gmlOffering;\r\n\r\nexport const kmlOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/kml' as const;\r\nexport type KML_Offering = typeof kmlOffering;\r\n\r\nexport const GeoTIFFOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/geotiff' as const;\r\nexport type GeoTIFF_Offering = typeof GeoTIFFOffering;\r\n\r\nexport const GMLJP2Offering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/gmljp2' as const;\r\nexport type GMLJP2_Offering = typeof GMLJP2Offering;\r\n\r\nexport const GMLCOVOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/gmlcov' as const;\r\nexport type GMLCOV_Offering = typeof GMLCOVOffering;\r\n","import {\r\n IOwsContext, IOwsResource, IOwsOffering, WFS_Offering, WCS_Offering, WPS_Offering,\r\n CSW_Offering, WMTS_Offering, GML_Offering, KML_Offering, GeoTIFF_Offering, GMLJP2_Offering,\r\n GMLCOV_Offering, IOwsResourceProperties, WMS_Offering\r\n} from './owc-json';\r\n\r\n\r\nimport * as GeoJSON from 'geojson';\r\nexport interface IEocOwsContext extends IOwsContext {\r\n features: IEocOwsResource[];\r\n /** @deprecated we do not use this currently */\r\n projections?: IEocOwsProjection[];\r\n}\r\n\r\nexport interface IEocOwsResource extends IOwsResource {\r\n properties: IEocOwsResourceProperties;\r\n}\r\n\r\nexport interface IEocOwsResourceProperties extends IOwsResourceProperties {\r\n /** The opacity of the displayed Layer */\r\n opacity?: number;\r\n attribution?: string; /** maybe this should be in IOwsResourceProperties.rights */\r\n /** Subdomains for urls in layers - e.g. 'a-d' is placed in https://{s}.tiles.geoservice.dlr.de/... as {a-d} or multiple urls are generated\r\n * e.g. https://a.tiles..., https://b.tiles...\r\n */\r\n shards?: string;\r\n /** Layer Dimension like Time and Elevation - To define e.g. the available Time data points/ranges in the Layer and a hint how to display them */\r\n dimensions?: IEocOwsResourceDimension[];\r\n /** Alternative to IOwsResourceProperties.minscaledenominator; easier to calculate in browser-apps */\r\n minZoom?: number;\r\n /** Alternative to IOwsResourceProperties.maxscaledenominator; easier to calculate in browser-apps */\r\n maxZoom?: number;\r\n /**\r\n * Folder is already defined on IOwsResourceProperties, this should only show how ukis is using it.\r\n * - string - Single Folder inside the Layers Slot `Layers`\r\n * - `${TFiltertypes}/string` - Single Folder inside one of the Layers Slots `TFiltertypes`\r\n */\r\n folder?: string;\r\n}\r\n\r\n\r\n\r\ntype isoInterval = `${string}/${string}`;\r\ntype intervalPeriod = `${isoInterval}/P${string}`;\r\n\r\nexport interface IEocOwsTimeDimension {\r\n name: 'time';\r\n /**\r\n * For time:\r\n * - '1984-01-01T00:00:00.000Z,1990-01-01T00:00:00.000Z,1995-01-01T00:00:00.000Z,...'\r\n * - '2000-09-01T00:00:00.000Z/2017-08-31T00:00:00.000Z/P1D'\r\n * - '2000-09-01T00:00:00.000Z/2010-08-31T00:00:00.000Z/P1D,2010-09-01T00:00:00.000Z/2020-08-31T00:00:00.000Z/P1D,...'\r\n * - '1984-01-01T00:00:00.000Z/P1Y,1985-01-01T00:00:00.000Z/P1Y,1986-01-01T00:00:00.000Z,1987-01-01T00:00:00.000Z,...'\r\n * also see https://moment.github.io/luxon/api-docs/index.html#intervalfromiso\r\n */\r\n values: `${string | isoInterval | intervalPeriod},${string | isoInterval | intervalPeriod}` | isoInterval | intervalPeriod;\r\n /**\r\n * For time: 'ISO8601'\r\n * ISO8601 has been chosen because this is how\r\n * geoserver's GetCapabilities response exposes\r\n * time information.\r\n */\r\n units: 'ISO8601' | string;\r\n display?: {\r\n /** format how to display the values e.g. YYYY-MM-DD */\r\n format?: string;\r\n /** in case the app should display data at a different period than what is available on the server */\r\n period?: string;\r\n /** The value which should be shown/used as default */\r\n default?: string;\r\n };\r\n}\r\n\r\n/** 12-111r1_Best_Practices_for_WMS_with_Time_or_Elevation_dependent_data.pdf - https://portal.ogc.org/files/?artifact_id=56394 */\r\nexport interface IEocOwsElevationDimension {\r\n name: 'elevation';\r\n /**\r\n *\r\n */\r\n value: string;\r\n /**\r\n * string or range\r\n * 100,200,300...\r\n * 100/1000\r\n */\r\n units: string;\r\n display?: {\r\n unitSymbol?: string;\r\n format?: string;\r\n /** in case the app should display data at a different elevation step */\r\n step?: string;\r\n /** The value which should be shown/used as default */\r\n default?: string;\r\n };\r\n}\r\n\r\nexport type IEocOwsResourceDimension = IEocOwsTimeDimension | IEocOwsElevationDimension;\r\n\r\nexport interface IEocOwsOffering extends IOwsOffering {\r\n code: WMS_Offering | WFS_Offering | WCS_Offering | WPS_Offering | CSW_Offering |\r\n WMTS_Offering | GML_Offering | KML_Offering | GeoTIFF_Offering | GMLJP2_Offering |\r\n GMLCOV_Offering | GeoJson_Offering | TMS_Offering | string;\r\n /** @deprecated we do not use this currently */\r\n iconUrl?: string;\r\n /** @deprecated we do not use this currently */\r\n title?: string;\r\n /** only for WMTS_Offering */\r\n matrixSets?: IEocOwsWmtsMatrixSet[];\r\n}\r\n\r\nexport interface IEocOwsWmtsMatrixSet {\r\n /** EPSG-Code */\r\n srs: string;\r\n matrixSet: string;\r\n matrixIds: string[];\r\n origin: {\r\n x: number,\r\n y: number\r\n };\r\n resolutions: number[];\r\n tilesize: {\r\n height: number,\r\n width: number\r\n };\r\n}\r\n\r\n/**\r\n * @deprecated we do not use this currently\r\n */\r\nexport interface IEocOwsProjection {\r\n bbox?: GeoJSON.BBox;\r\n code: string;\r\n default?: boolean;\r\n unit?: string | number;\r\n}\r\n/**\r\n * http://www.owscontext.org/owc_user_guide/C0_userGuide.html#trueextension-offerings\r\n */\r\nexport const GeoJsonOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/geojson' as const;\r\nexport type GeoJson_Offering = typeof GeoJsonOffering;\r\n\r\nexport const xyzOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/xyz' as const;\r\nexport type Xyz_Offering = typeof xyzOffering;\r\n\r\nexport const tmsOffering = 'http://www.opengis.net/spec/owc-geojson/1.0/req/tms' as const;\r\nexport type TMS_Offering = typeof tmsOffering;\r\n\r\n\r\n/** list of context files */\r\nexport interface IEocOwsContextListItem {\r\n id: IEocOwsContext['id'];\r\n /** relative or absolute link/path to context file */\r\n url: string;\r\n /** default is true */\r\n enabled?: boolean;\r\n}\r\n\r\nexport type EocOwsContextList = IEocOwsContextListItem[];\r\n","/** This file contains functions (Type Guards) to test for types in owc-json.ts */\r\n\r\nimport { Xyz_Offering, GeoJson_Offering, TMS_Offering, GeoJsonOffering, tmsOffering, xyzOffering } from './eoc-owc-json';\r\nimport { cswOffering, CSW_Offering, GeoTIFFOffering, GeoTIFF_Offering, GMLCOVOffering, GMLCOV_Offering, GMLJP2Offering, GMLJP2_Offering, gmlOffering, GML_Offering, IOwsAuthor, IOwsCategory, IOwsContent, IOwsContext, IOwsGenerator, IOwsCreatorDisplay, IOwsLinks, IOwsOffering, IOwsOperation, IOwsResource, IOwsResourceProperties, IOwsStyleSet, kmlOffering, KML_Offering, wcsOffering, WCS_Offering, wfsOffering, WFS_Offering, wmsOffering, WMS_Offering, wmtsOffering, WMTS_Offering } from './owc-json';\r\n\r\n\r\n/**\r\n * export types to create layers from Offerings\r\n */\r\nexport const GetMapOperationCode = 'GetMap' as const;\r\nexport type WMS_Code = typeof GetMapOperationCode;\r\n\r\nexport const GetFeatureOperationCode = 'GetFeature' as const;\r\nexport type WFS_Code = typeof GetFeatureOperationCode;\r\n\r\nexport const GetTileOperationCode = 'GetTile' as const;\r\nexport type WMTS_Code = typeof GetTileOperationCode;\r\n\r\nexport const RESTOperationCode = 'REST' as const;\r\nexport type TMS_Code = typeof RESTOperationCode;\r\nexport type XYZ_Code = typeof RESTOperationCode;\r\n\r\nexport const GetCapabilitiesOperationCode = 'GetCapabilities' as const;\r\nexport const DescribeFeatureTypeOperationCode = 'DescribeFeatureType' as const;\r\nexport const GetFeatureInfoOperationCode = 'GetFeatureInfo' as const;\r\n\r\n\r\nfunction trueForAll(list: any[], predicate: (o: any) => boolean): boolean {\r\n for (const entry of list) {\r\n if (!predicate(entry)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\nexport function isIOwsContext(object: IOwsContext): object is IOwsContext {\r\n let ISCONTEXT_1_0;\r\n if (object?.properties?.links) {\r\n ISCONTEXT_1_0 = object.properties.links.profiles.find(item => item.href === 'http://www.opengis.net/spec/owc-geojson/1.0/req/core');\r\n }\r\n\r\n if (!ISCONTEXT_1_0) {\r\n console.error('this is not a valid OWS Context v1.0!');\r\n return false;\r\n } else {\r\n return true;\r\n }\r\n}\r\n\r\nexport function isIOwsResource(object: any): object is IOwsResource {\r\n return 'id' in object && 'type' in object\r\n && 'properties' in object && isIOwsResourceProperties(object.properties);\r\n}\r\n\r\nexport function isIOwsResourceProperties(object: any): object is IOwsResourceProperties {\r\n return 'title' in object\r\n && 'updated' in object\r\n && (object.authors ? trueForAll(object.authors, isIOwsAuthor) : true)\r\n && (object.offerings ? trueForAll(object.offerings, isIOwsOffering) : true)\r\n && (object.categories ? trueForAll(object.categories, isIOwsCategory) : true);\r\n}\r\n\r\nexport function isIOwsOffering(object: any): object is IOwsOffering {\r\n return 'code' in object\r\n && (object.operations ? trueForAll(object.operations, isIOwsOperation) : true)\r\n && (object.contents ? trueForAll(object.contents, isIOwsContent) : true)\r\n && (object.styles ? trueForAll(object.styles, isIOwsStyleSet) : true)\r\n}\r\n\r\nexport function isIOwsGenerator(object: any): object is IOwsGenerator {\r\n return 'title' in object\r\n || 'uri' in object\r\n || 'version' in object;\r\n}\r\n\r\nexport function isIOwsAuthor(object: any): object is IOwsAuthor {\r\n return 'name' in object\r\n || 'email' in object\r\n || 'uri' in object;\r\n}\r\n\r\nexport function isIOwsCategory(object: any): object is IOwsCategory {\r\n return 'scheme' in object\r\n || 'term' in object\r\n || 'label' in object;\r\n}\r\n\r\nexport function isIOwsLinks(object: any): object is IOwsLinks {\r\n return 'rel' in object;\r\n}\r\n\r\nexport function isIOwsCreatorDisplay(object: any): object is IOwsCreatorDisplay {\r\n return 'pixelWidth' in object\r\n || 'pixelHeight' in object\r\n || 'mmPerPixel' in object;\r\n}\r\n\r\nexport function isIOwsOperation(object: any): object is IOwsOperation {\r\n return 'code' in object\r\n && 'method' in object\r\n && (object.request ? isIOwsContent(object.request) : true)\r\n && (object.result ? isIOwsContent(object.result) : true);\r\n}\r\n\r\nexport function isIOwsRasterOperation(object: any): object is IOwsOperation {\r\n if (isIOwsOperation(object)) {\r\n return [GetMapOperationCode, GetTileOperationCode, RESTOperationCode].includes(object.code as any);\r\n } else {\r\n return false;\r\n }\r\n}\r\n\r\nexport function isIOwsVectorOperation(object: any): object is IOwsOperation {\r\n if (isIOwsOperation(object)) {\r\n return [GetFeatureOperationCode].includes(object.code as any);\r\n } else {\r\n return false;\r\n }\r\n}\r\n\r\nexport function isIOwsContent(object: any): object is IOwsContent {\r\n return 'type' in object;\r\n}\r\n\r\nexport function isIOwsStyleSet(object: any): object is IOwsStyleSet {\r\n return 'name' in object\r\n && 'title' in object;\r\n}\r\n\r\nexport function isWmsOffering(str: string): str is WMS_Offering {\r\n return str === wmsOffering;\r\n}\r\nexport function isWfsOffering(str: string): str is WFS_Offering {\r\n return str === wfsOffering;\r\n}\r\nexport function isWpsOffering(str: string): str is WCS_Offering {\r\n return str === wcsOffering;\r\n}\r\nexport function isCswOffering(str: string): str is CSW_Offering {\r\n return str === cswOffering;\r\n}\r\nexport function isWmtsOffering(str: string): str is WMTS_Offering {\r\n return str === wmtsOffering;\r\n}\r\nexport function isGmlOffering(str: string): str is GML_Offering {\r\n return str === gmlOffering;\r\n}\r\nexport function isKmlOffering(str: string): str is KML_Offering {\r\n return str === kmlOffering;\r\n}\r\nexport function isGeoTIFFOffering(str: string): str is GeoTIFF_Offering {\r\n return str === GeoTIFFOffering;\r\n}\r\nexport function isGMLJP2Offering(str: string): str is GMLJP2_Offering {\r\n return str === GMLJP2Offering;\r\n}\r\nexport function isGMLCOVOffering(str: string): str is GMLCOV_Offering {\r\n return str === GMLCOVOffering;\r\n}\r\nexport function isXyzOffering(str: string): str is Xyz_Offering {\r\n return str === xyzOffering;\r\n}\r\nexport function isGeoJsonOffering(str: string): str is GeoJson_Offering {\r\n return str === GeoJsonOffering;\r\n}\r\nexport function isTMSOffering(str: string): str is TMS_Offering {\r\n return str === tmsOffering;\r\n}\r\n","import { Injectable } from '@angular/core';\r\nimport { HttpClient, HttpHeaders } from '@angular/common/http';\r\nimport { Observable } from 'rxjs';\r\nimport { Jsonix } from '@michaellangbein/jsonix';\r\nimport { map } from 'rxjs/operators';\r\nimport * as XLink_1_0_Factory from 'w3c-schemas/lib/XLink_1_0';\r\nconst XLink_1_0 = XLink_1_0_Factory.XLink_1_0;\r\nimport * as OWS_1_1_0_Factory from 'ogc-schemas/lib/OWS_1_1_0';\r\nconst OWS_1_1_0 = OWS_1_1_0_Factory.OWS_1_1_0;\r\nimport * as SMIL_2_0_Factory from 'ogc-schemas/lib/SMIL_2_0';\r\nconst SMIL_2_0 = SMIL_2_0_Factory.SMIL_2_0;\r\nimport * as SMIL_2_0_Language_Factory from 'ogc-schemas/lib/SMIL_2_0_Language';\r\nconst SMIL_2_0_Language = SMIL_2_0_Language_Factory.SMIL_2_0_Language;\r\nimport * as GML_3_1_1_Factory from 'ogc-schemas/lib/GML_3_1_1';\r\nconst GML_3_1_1 = GML_3_1_1_Factory.GML_3_1_1;\r\nimport * as WMTS_1_0_Factory from 'ogc-schemas/lib/WMTS_1_0';\r\nconst WMTS_1_0 = WMTS_1_0_Factory.WMTS_1_0;\r\n\r\n\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class WmtsClientService {\r\n\r\n private xmlmarshaller;\r\n private xmlunmarshaller;\r\n\r\n constructor(private http: HttpClient) {\r\n const context = new Jsonix.Context([SMIL_2_0, SMIL_2_0_Language, GML_3_1_1, XLink_1_0, OWS_1_1_0, WMTS_1_0]);\r\n this.xmlunmarshaller = context.createUnmarshaller();\r\n this.xmlmarshaller = context.createMarshaller();\r\n }\r\n\r\n public getCapabilities(url: string, version = '1.1.0'): Observable<object> {\r\n // example: https://tiles.geoservice.dlr.de/service/wmts?SERVICE=WMTS&REQUEST=GetCapabilities&VERSION=1.1.0\r\n const getCapabilitiesUrl = `${url}?SERVICE=WMTS&REQUEST=GetCapabilities&VERSION=${version}`;\r\n const headers = new HttpHeaders({\r\n 'Content-Type': 'text/xml',\r\n Accept: 'text/xml, application/xml'\r\n });\r\n return this.http.get(getCapabilitiesUrl, { headers, responseType: 'text' }).pipe(\r\n map(response => {\r\n return this.xmlunmarshaller.unmarshalString(response);\r\n })\r\n );\r\n }\r\n\r\n}\r\n","\r\nimport { Injectable } from '@angular/core';\r\nimport {\r\n IOwsContext, IOwsResource, IOwsOffering, IOwsOperation, IOwsContent, kmlOffering, wfsOffering, wmsOffering, wmtsOffering\r\n} from './types/owc-json';\r\nimport { DescribeFeatureTypeOperationCode, GetCapabilitiesOperationCode, GetFeatureInfoOperationCode, GetFeatureOperationCode, GetMapOperationCode, GetTileOperationCode, isGeoJsonOffering, isIOwsContext, isIOwsRasterOperation, isKmlOffering, isTMSOffering, isWfsOffering, isWmsOffering, isWmtsOffering, isXyzOffering, RESTOperationCode } from './types/owc-json.utils';\r\nimport {\r\n IEocOwsContext, IEocOwsResource, IEocOwsOffering, IEocOwsWmtsMatrixSet,\r\n IEocOwsResourceDimension,\r\n IEocOwsTimeDimension,\r\n IEocOwsElevationDimension,\r\n GeoJsonOffering,\r\n xyzOffering,\r\n tmsOffering\r\n} from './types/eoc-owc-json';\r\nimport {\r\n ILayerOptions, IRasterLayerOptions, VectorLayer, RasterLayer, IVectorLayerOptions,\r\n Layer, TLayertype, WmsLayertype, WmtsLayertype, WfsLayertype, GeojsonLayertype, XyzLayertype,\r\n TRasterLayertype, ILayerDimensions,\r\n ILayerIntervalAndPeriod,\r\n WmtsLayer,\r\n IWmtsOptions,\r\n WmsLayer,\r\n IWmsParams,\r\n IWmsOptions,\r\n IListMatrixSet,\r\n TFiltertypes,\r\n LayerGroup,\r\n ILayerTimeDimension,\r\n ILayerElevationDimension,\r\n Filtertypes,\r\n TmsLayertype,\r\n KmlLayertype,\r\n IWmtsParams,\r\n TVectorLayertype,\r\n StackedLayer,\r\n IStackedLayerOptions\r\n} from '@dlr-eoc/services-layers';\r\nimport { TGeoExtent } from '@dlr-eoc/services-map-state';\r\nimport { WmtsClientService } from '../wmts/wmtsclient.service';\r\nimport { of, Observable, forkJoin, concat } from 'rxjs';\r\nimport { filter, map } from 'rxjs/operators';\r\n\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { DateTime, Interval } from 'luxon';\r\nimport { get as getProjection } from 'ol/proj';\r\n\r\n\r\nexport function shardsExpand(v: string) {\r\n if (!v) { return; }\r\n const o: string[] = [];\r\n const shardsSplit = v.split(',');\r\n for (const i in shardsSplit) {\r\n if (shardsSplit[i]) {\r\n const j = shardsSplit[i].split('-');\r\n if (j.length === 1) {\r\n o.push(shardsSplit[i]);\r\n } else if (j.length === 2) {\r\n const start = j[0].charCodeAt(0);\r\n const end = j[1].charCodeAt(0);\r\n if (start <= end) {\r\n for (let k = start; k <= end; k++) {\r\n o.push(String.fromCharCode(k).toLowerCase());\r\n }\r\n } else {\r\n for (let k = start; k >= end; k--) {\r\n o.push(String.fromCharCode(k).toLowerCase());\r\n }\r\n }\r\n }\r\n }\r\n }\r\n return o;\r\n}\r\n\r\n/**\r\n * OWS Context Service\r\n * OGC OWS Context Geo Encoding Standard Version: 1.0\r\n * http://docs.opengeospatial.org/is/14-055r2/14-055r2.html\r\n * http://www.owscontext.org/owc_user_guide/C0_userGuide.html\r\n *\r\n * This service allows you to read and write OWC-data.\r\n * We have added some custom fields to the OWC standard.\r\n * - accepts the OWC-standard-data-types as function inputs (so as to be as general as possible)\r\n * - returns our extended OWC-data-types as function outputs (so as to be as information-rich as possible)\r\n *\r\n * As a policy, this services does *not* make any HTTP requests to GetCapabilities (or similar) to gather\r\n * additional information (with very few exceptions) - we want to save on network traffic.\r\n * However there are scripts that auto-generate OWC files from Capabilities, those, of course,\r\n * *do* scrape as much information online as possible; But they are not intended to be used in\r\n * a live-application. Run them batch-wise and server-side instead.\r\n */\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class OwcJsonService {\r\n\r\n constructor(\r\n private wmtsClient: WmtsClientService,\r\n private http: HttpClient) {\r\n // http://www.owscontext.org/owc_user_guide/C0_userGuide.html#truegeojson-encoding-2\r\n }\r\n\r\n\r\n checkContext(context: IOwsContext) {\r\n return isIOwsContext(context);\r\n }\r\n\r\n getContextTitle(context: IOwsContext) {\r\n return context.properties.title;\r\n }\r\n\r\n getContextPublisher(context: IOwsContext) {\r\n return (context.properties.publisher) ? context.properties.publisher : null;\r\n }\r\n\r\n getContextExtent(context: IOwsContext) {\r\n return (context.bbox) ? context.bbox : null; // or [-180, -90, 180, 90];\r\n }\r\n\r\n getResources(context: IOwsContext): IOwsResource[] {\r\n return context.features;\r\n }\r\n\r\n /**\r\n * Get Resources whith Folder property but not including Layer-Filtertypes\r\n */\r\n getGroupResources(context: IOwsContext): IOwsResource[] {\r\n const resources = context.features;\r\n return resources.filter(r => {\r\n const groupName = this.getLayerGroupFromFolder(r);\r\n return groupName && !Object.keys(Filtertypes).includes(groupName);\r\n });\r\n }\r\n\r\n /**\r\n * Get Resources without Folder property or Folder is only Layer-Filtertypes\r\n */\r\n getSingleResources(context: IOwsContext): IOwsResource[] {\r\n const resources = context.features;\r\n return resources.filter(r => {\r\n const groupName = this.getLayerGroupFromFolder(r);\r\n return !groupName || Object.keys(Filtertypes).includes(groupName);\r\n });\r\n }\r\n\r\n /** Resource --------------------------------------------------- */\r\n getResourceTitle(resource: IOwsResource): string {\r\n return resource.properties.title;\r\n }\r\n\r\n /**\r\n * The Folder property of IOwsResource\r\n * @returns string | `${TFiltertypes}/string`\r\n */\r\n getResourceFolder(resource: IOwsResource): string {\r\n return resource.properties.folder;\r\n }\r\n\r\n /**\r\n * returns name from Resource Folder if it is not only a Filtertype `TFiltertypes`\r\n */\r\n private getLayerGroupFromFolder(resource: IOwsResource) {\r\n const folderName = this.getResourceFolder(resource);\r\n if (folderName) {\r\n const folderParts = folderName.split('/');\r\n if (folderParts.length === 1) {\r\n if (!Filtertypes[folderName]) {\r\n return folderName\r\n }\r\n } else if (folderParts.length === 2) {\r\n const filtertype = folderParts[0];\r\n if (!Filtertypes[filtertype]) {\r\n console.warn(`Folder (${folderName}) should be named like: ${Object.keys(Filtertypes).map(k => `${k}/<FolderName>`).join(' | ')}`);\r\n }\r\n return folderParts[1];\r\n } else {\r\n console.log(`only one Folder hierarchy is implemented`, folderParts);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * FilterType in IOwsResource Folder property\r\n */\r\n getFilterType(resource: IOwsResource): TFiltertypes {\r\n if (resource.properties.folder) {\r\n const pathParts = resource.properties.folder.split('/');\r\n const first = pathParts[0];\r\n if (Filtertypes[first]) {\r\n return first as TFiltertypes;\r\n }\r\n }\r\n }\r\n\r\n getResourceUpdated(resource: IOwsResource) {\r\n return resource.properties.updated;\r\n }\r\n\r\n getResourceDate(resource: IOwsResource) {\r\n return (resource.properties.date) ? resource.properties.date : null;\r\n }\r\n\r\n getResourceOfferings(resource: IOwsResource): IOwsOffering[] {\r\n return (resource.properties.offerings) ? resource.properties.offerings : null;\r\n }\r\n\r\n /**\r\n * retrieve layer status active / inactive based on IOwsResource\r\n * @param resource: IOwsResource\r\n */\r\n isActive(resource: IOwsResource) {\r\n let active = true;\r\n if (resource.properties.active === false || resource.properties?.active) {\r\n active = resource.properties.active;\r\n }\r\n return active;\r\n }\r\n\r\n getResourceDescription(resource: IOwsResource): string {\r\n let description = '';\r\n if (resource.properties.abstract) {\r\n description = resource.properties.abstract;\r\n }\r\n return description;\r\n }\r\n\r\n /** OWS Extenson IEocOwsResource */\r\n getResourceOpacity(resource: IEocOwsResource): number {\r\n let opacity = 1;\r\n if (resource.properties?.opacity) {\r\n opacity = resource.properties.opacity;\r\n }\r\n return opacity;\r\n }\r\n\r\n /** OWS Extenson IEocOwsResource */\r\n getResourceAttribution(resource: IEocOwsResource): string {\r\n let attribution = '';\r\n if (resource.properties?.attribution) {\r\n attribution = resource.properties.attribution;\r\n } else if (resource.properties.rights) {\r\n attribution = resource.properties.rights;\r\n }\r\n return attribution;\r\n }\r\n\r\n /** OWS Extenson IEocOwsResource */\r\n getResourceShards(resource: IEocOwsResource): string {\r\n if (resource.properties?.shards) {\r\n return resource.properties.shards;\r\n }\r\n }\r\n\r\n /** OWS Extenson IEocOwsResource */\r\n getResourceMinMaxZoom(resource: IEocOwsResource, targetProjection: string = 'EPSG:4326'): { minZoom: number; maxZoom: number; } {\r\n const zooms = { minZoom: null, maxZoom: null };\r\n if (resource.properties.minZoom) {\r\n zooms.minZoom = resource.properties.minZoom;\r\n } else if (resource.properties.maxscaledenominator) { // *Max*ScaleDenom ~ *Min*Zoom\r\n zooms.minZoom = this.scaleDenominatorToZoom(resource.properties.maxscaledenominator, targetProjection) || null;\r\n }\r\n if (resource.properties.maxZoom) {\r\n zooms.maxZoom = resource.properties.maxZoom;\r\n } else if (resource.properties.minscaledenominator) { // *Min*ScaleDenom ~ *Max*Zoom\r\n zooms.maxZoom = this.scaleDenominatorToZoom(resource.properties.minscaledenominator, targetProjection) || null;\r\n }\r\n return zooms;\r\n }\r\n\r\n\r\n /**\r\n * e.g.\r\n * (array) value: '1984-01-01T00:00:00.000Z/1989-12-31T23:59:59.000Z/PT1S,1990-01-01T00:00:00.000Z/1994-12-31T23:59:59.000Z/PT1S,...'\r\n * (array) value: '1984-01-01T00:00:00.000Z/P1D,P1D/2000-01-01T00:00:00.000Z,...'\r\n * (array) value: '2000-01-01T00:00:00.000Z,2001-01-01T00:00:00.000Z,2002-01-01T00:00:00.000Z,...'\r\n * (single) value: '2016-01-01T00:00:00.000Z/2018-01-01T00:00:00.000Z/P1Y'\r\n */\r\n getTimeValueFromDimensions(values: IEocOwsTimeDimension['values'], period?: IEocOwsTimeDimension['display']['period']): ILayerIntervalAndPeriod | Array<string | ILayerIntervalAndPeriod> {\r\n if (values === null) {\r\n return;\r\n } else {\r\n const isList = /,/g.test(values);\r\n if (isList) {\r\n // values: `${string},${string}`\r\n const splitValues = values.split(',');\r\n if (splitValues.length > 0) {\r\n const parsed: Array<string | ILayerIntervalAndPeriod> = []; //\r\n for (const value of splitValues) {\r\n const parsedSingle = this.parseSingleTimeOrPeriod(value);\r\n if (typeof parsedSingle === 'object' && parsedSingle.interval) {\r\n if (!parsedSingle.periodicity && period) {\r\n parsedSingle.periodicity = period;\r\n } else if (!parsedSingle.periodicity && !period) {\r\n console.warn(`Interval without a period`, values, period);\r\n }\r\n }\r\n parsed.push(parsedSingle);\r\n }\r\n return parsed;\r\n }\r\n } else {\r\n // `${string}/${string}` | `${string}/${string}/P${string}`\r\n const parsedSingle = this.parseSingleTimeOrPeriod(values);\r\n if (typeof parsedSingle === 'object' && parsedSingle.interval) {\r\n if (!parsedSingle.periodicity && period) {\r\n parsedSingle.periodicity = period;\r\n } else if (!parsedSingle.periodicity && !period) {\r\n console.warn(`Interval without a period`, values, period);\r\n }\r\n return parsedSingle;\r\n } else if (typeof parsedSingle === 'string') {\r\n return [parsedSingle];\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * time could be:\r\n *\r\n * - date\r\n * - start/end/duration //Geoserver specific\r\n * - start/end\r\n * - start/duration, and duration/end\r\n */\r\n private parseSingleTimeOrPeriod(time: string): string | ILayerIntervalAndPeriod | null {\r\n const dateTime = DateTime.fromISO(time);\r\n if (dateTime.isValid) {\r\n return dateTime.toUTC().toISO();\r\n } else {\r\n // is Interval ----------------------------\r\n const interval = Interval.fromISO(time);\r\n if (interval.isValid) {\r\n const period = this.parseISO8601Period(time);\r\n const intervalObject: ILayerIntervalAndPeriod = {\r\n periodicity: period,\r\n interval: `${interval.start.toUTC().toISO()}/${interval.end.toUTC().toISO()}`\r\n };\r\n return intervalObject;\r\n } else {\r\n console.warn(`no Interval or not valid`, time);\r\n return null;\r\n }\r\n }\r\n }\r\n\r\n private parseISO8601Period(value: string): string {\r\n const periodMatches = value.match(/P\\d*[YMWD](T\\d\\d[HMS])*/);\r\n if (periodMatches) {\r\n return periodMatches[0];\r\n }\r\n }\r\n\r\n getResourceDimensions(resource: IEocOwsResource) {\r\n if (!resource.properties.dimensions) {\r\n return undefined;\r\n }\r\n\r\n const dims: ILayerDimensions = {};\r\n for (const d of resource.properties.dimensions) {\r\n const name = d.name;\r\n if (name === 'time') {\r\n dims.time = this.getTimeDimensions(resource.properties.dimensions);\r\n /** if dimensions are defined but the values are null */\r\n if (dims.time.values === null) {\r\n console.log('check to get time dimensions value from OGC Service later!!', resource);\r\n }\r\n } else if (name === 'elevation') {\r\n dims.elevation = this.getElevationDimension(resource.properties.dimensions);\r\n /** if dimensions are defined but the values are null */\r\n if (dims.elevation.values === null) {\r\n console.log('check to get elevation dimensions value from OGC Service later!!', resource);\r\n }\r\n } else {\r\n dims[name] = d;\r\n }\r\n }\r\n\r\n return dims;\r\n }\r\n\r\n getTimeDimensions(dimensions: IEocOwsResourceDimension[]): ILayerTimeDimension {\r\n let dim: ILayerTimeDimension = { values: null, units: null };\r\n const value = dimensions.find(d => d.name === 'time') as IEocOwsTimeDimension;\r\n if (!value) {\r\n return;\r\n }\r\n\r\n const parsedValues = this.getTimeValueFromDimensions(value.values, value?.display?.period);\r\n dim = {\r\n values: null,\r\n units: value.units,\r\n display: {}\r\n };\r\n\r\n /** check if is array or single value */\r\n if (Array.isArray(parsedValues)) {\r\n dim.values = parsedValues as (string[] | ILayerIntervalAndPeriod[]);\r\n /** don't set dim.display.period if it is an array because there could be different periods */\r\n // dim.display.period = ...\r\n } else if (parsedValues && typeof parsedValues !== 'string' && parsedValues.interval && parsedValues.periodicity) {\r\n dim.values = parsedValues;\r\n /** set dim.display.period from the parsed values */\r\n if (parsedValues.periodicity) {\r\n dim.display.period = parsedValues.periodicity;\r\n }\r\n }\r\n\r\n if (value?.display?.format) {\r\n dim.display.format = value.display.format;\r\n }\r\n\r\n return dim;\r\n }\r\n\r\n getElevationDimension(dimensions: IEocOwsResourceDimension[]): ILayerElevationDimension {\r\n const dim: ILayerElevationDimension = { values: null, units: null };\r\n const value = dimensions.find(d => d.name === 'elevation') as IEocOwsElevationDimension;\r\n if (!value) {\r\n return;\r\n } else {\r\n dim.values = value.value;\r\n dim.units = value.units;\r\n\r\n if (value.display) {\r\n dim.display = value.display;\r\n }\r\n\r\n return dim;\r\n }\r\n }\r\n\r\n\r\n /** Offering --------------------------------------------------- */\r\n getLayertypeFromOfferingCode(offering: IOwsOffering): TLayertype {\r\n if (isWmsOffering(offering.code)) {\r\n return WmsLayertype;\r\n } else if (isWmtsOffering(offering.code)) {\r\n return WmtsLayertype;\r\n } else if (isWfsOffering(offering.code)) {\r\n return WfsLayertype;\r\n } else if (isKmlOffering(offering.code)) {\r\n return KmlLayertype;\r\n } else if (isGeoJsonOffering(offering.code)) {\r\n return GeojsonLayertype;\r\n } else if (isXyzOffering(offering.code)) {\r\n return XyzLayertype;\r\n } else if (isTMSOffering(offering.code)) {\r\n return TmsLayertype;\r\n } else {\r\n return offering.code; // an offering can also be any other string.\r\n }\r\n }\r\n\r\n checkIfServiceOffering(offering: IOwsOffering): boolean {\r\n return (!offering.contents && offering.operations) ? true : false;\r\n }\r\n\r\n checkIfDataOffering(offering: IOwsOffering): boolean {\r\n return (offering.contents && !offering.operations) ? true : false;\r\n }\r\n\r\n /**\r\n * Helper function to extract legendURL from project specific ows Context\r\n * @param offering layer offering\r\n */\r\n getLegendUrl(offering: IOwsOffering) {\r\n let legendUrl = '';\r\n\r\n if (offering.styles) {\r\n const defaultStyle = offering.styles.find(style => style.default);\r\n if (defaultStyle) {\r\n return defaultStyle.legendURL;\r\n }\r\n } else if (offering.legendUrl) {\r\n legendUrl = offering.legendUrl;\r\n }\r\n return legendUrl;\r\n }\r\n\r\n\r\n /**\r\n * Get all Layers from the IOwsContext.\r\n *\r\n * The order of the layers is reversed to get the context drawing order!\r\n */\r\n getLayers(owc: IOwsContext, targetProjection: string): Observable<(Layer | LayerGroup)[]> {\r\n const layers$: Observable<Layer | LayerGroup>[] = [];\r\n /** For the order of Layers see IOwsContext['features'] */\r\n\r\n /**\r\n * LayerGroups\r\n *\r\n * e.g. if groupName: Layers/test -> a group \"test\" in the slot Layers will be created with the layer in it\r\n * e.g. if groupName: Overlays/test -> a group \"test\" in the slot Overlays will be created with the layer in it\r\n * if groupName is only: Layers | Overlays | Baselayers use layerResources\r\n */\r\n\r\n const resources = this.getResources(owc);\r\n const groups = [];\r\n resources.forEach(r => {\r\n const lg = this.createLayerOrGroupFromResource(r, owc, targetProjection, groups);\r\n layers$.push(lg);\r\n });\r\n\r\n return forkJoin(layers$)\r\n // making sure no undefined/null layers are returned\r\n .pipe(map(layers => layers.filter(layer => layer)))\r\n // reverse so layer order is like in the context\r\n .pipe(map(layers => layers.reverse()));\r\n }\r\n\r\n /**\r\n * Creates Layers or LayerGroups from IOwsResource and IOwsContext\r\n * Add uniqueGroups array to track already created groups\r\n */\r\n private createLayerOrGroupFromResource(resource: IOwsResource, context: IOwsContext, targetProjection: string, uniqueGroups: string[]) {\r\n const layergroupResources = this.getGroupResources(context);\r\n const groupName = this.getLayerGroupFromFolder(resource);\r\n\r\n /** Layers with folder property */\r\n if (groupName) {\r\n /** unique layergroupResources */\r\n if (!uniqueGroups.includes(groupName)) {\r\n uniqueGroups.push(groupName);\r\n /** reverse so layer order is like in the context */\r\n const includedResources = layergroupResources.filter(r => this.getLayerGroupFromFolder(r) === groupName).reverse();\r\n const layerGroup$ = this.createLayerGroup(groupName, includedResources, context, targetProjection);\r\n return layerGroup$;\r\n } else {\r\n return of(null);\r\n }\r\n } else {\r\n /** Single Layers */\r\n const layer$ = this.createLayerFromDefaultOffering(resource, context, targetProjection);\r\n return layer$;\r\n }\r\n }\r\n\r\n\r\n\r\n /**\r\n *\r\n * @param groupName string | `${TFiltertypes}/string`\r\n */\r\n createLayerGroup(groupName: string, includedResources: IOwsResource[], owc: IOwsContext, targetProjection: string): Observable<LayerGroup | StackedLayer> {\r\n const layers$: Observable<Layer>[] = [];\r\n let filterType = null;\r\n for (const resource of includedResources) {\r\n filterType = this.getFilterType(resource);\r\n layers$.push(this.createLayerFromDefaultOffering(resource, owc, targetProjection));\r\n }\r\n\r\n const layerGroup$ = forkJoin(layers$)\r\n // making sure no undefined layers are returned\r\n .pipe(map((layers: Layer[]) => layers.filter(layer => layer)))\r\n // putting layers in a LayerGroup\r\n .pipe(map((layers: Layer[]) => {\r\n if (layers.length) {\r\n /** if filterType is Baselayers -> create a merged Layer */\r\n if (filterType === Filtertypes.Baselayers) {\r\n const descriptionLayers