File

libs/open-layers/src/lib/services/wms-capabilities.service.ts

Index

Properties

Properties

abstract
abstract: string
Type : string
childLayer
childLayer: WMSLayer[]
Type : WMSLayer[]
name
name: string
Type : string
title
title: string
Type : string
import { Injectable } from '@angular/core';
import { HttpService } from '@helgoland/core';
import WMSCapabilities from 'ol/format/WMSCapabilities';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

interface InternalWMSLayer {
  Name: string;
  Title: string;
  Abstract: string;
  Layer: InternalWMSLayer[];
  Dimension: {
    name: string;
    default: string;
    values: string;
  }[];
  BoundingBox: {
    crs: string;
    extent: number[]
  }[];
  Style: {
    Abstract: string;
    Name: string;
    Title: string;
    LegendURL: {
      Format: string;
      OnlineResource: string;
      size: number[];
    }[]
  }[];
  EX_GeographicBoundingBox: number[];
}

export interface WMSLayer {
  name: string;
  title: string;
  abstract: string;
  childLayer: WMSLayer[];
}

// request Capabilities only all 5 minutes
const WMS_CAPABILITIES_REQUEST_EXPIRATION = 1000 * 60 * 5;

/**
 * Handler for WMS capabilities
 */
@Injectable({
  providedIn: 'root'
})
export class WmsCapabilitiesService {

  constructor(
    private http: HttpService
  ) { }

  /**
   * Returns the layer title of the capabilities as observable
   *
   * @param layerName
   * @param wmsurl
   * @returns layer title as string observable
   */
  public getTitle(layerName: string, wmsurl: string): Observable<string> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => layer.Title));
  }

  /**
   * Returns the layer abstract of the capabilities as observable
   *
   * @param layerName
   * @param wmsurl
   * @returns layer abstract as string observable
   */
  public getAbstract(layerName: string, wmsurl: string): Observable<string> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => layer.Abstract));
  }

  /**
   * Returns a date array as observable for the given layername, if available
   *
   * @param layerName
   * @param wmsurl
   * @returns
   */
  public getTimeDimensionArray(layerName: string, wmsurl: string): Observable<Date[]> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => {
      const timeDimension = layer.Dimension.find(e => e.name = 'time');
      if (timeDimension) {
        return this.createTimeList(timeDimension);
      } else {
        return [];
      }
    }));
  }

  /**
   * Returns a legend url as observable, if available
   *
   * @param layerName
   * @param wmsurl
   * @returns
   */
  public getLegendUrl(layerName: string, wmsurl: string): Observable<string> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => {
      let legendUrl = '';
      layer.Style.forEach(s => s.LegendURL.forEach(l => legendUrl = l.OnlineResource));
      return legendUrl;
    }));
  }

  /**
   * Returns the default date as observable for the given layername, if available
   *
   * @param layerName
   * @param wmsurl
   * @returns
   */
  public getDefaultTimeDimension(layerName: string, wmsurl: string): Observable<Date> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => {
      const timeDimension = layer.Dimension.find(e => e.name = 'time');
      if (timeDimension && timeDimension.default) {
        if (timeDimension.default === 'current') {
          const timeList = this.createTimeList(timeDimension);
          return this.findNearestTimestamp(timeList, new Date());
        } else {
          return new Date(timeDimension.default);
        }
      } else {
        return null;
      }
    }));
  }

  /**
   * Returns the extent as observable for the given layername and epsg code, if available
   *
   * @param layerName
   * @param wmsurl
   * @param epsgCode
   * @returns
   */
  public getExtent(layerName: string, wmsurl: string, epsgCode: string): Observable<{ crs: string, extent: number[] }> {
    return this.getLayerInfo(layerName, wmsurl).pipe(map(layer => {
      const match = layer.BoundingBox.find(e => e.crs === epsgCode);
      if (match) {
        return this.fixExtent(match.crs, match.extent);
      } else {
        if (layer.BoundingBox.length > 0) {
          return this.fixExtent(layer.BoundingBox[0].crs, layer.BoundingBox[0].extent);
        }
      }
    }));
  }

  public getLayerTree(wmsurl: string): Observable<WMSLayer> {
    return this.getCapabilities(wmsurl).pipe(map(res => this.createLayer(res.Capability.Layer)));
  }

  /**
   * Removes every request parameter of the url an returns this cleand url.
   */
  public cleanUpWMSUrl(url: string): string {
    let wmsRequesturl = url;
    if (wmsRequesturl.indexOf('?') !== -1) {
      wmsRequesturl = wmsRequesturl.substring(0, wmsRequesturl.indexOf('?'));
    }
    return wmsRequesturl;
  }

  private createLayer(layer: InternalWMSLayer): WMSLayer {
    return {
      name: layer.Name,
      title: layer.Title,
      abstract: layer.Abstract,
      childLayer: layer.Layer ? layer.Layer.map(l => this.createLayer(l)) : []
    };
  }

  private getCapabilities(url: string): Observable<any> {
    const wmsRequesturl = this.cleanUpWMSUrl(url) + '?request=GetCapabilities&service=wms&version=1.3.0';
    return this.http.client({ expirationAtMs: new Date().getTime() + WMS_CAPABILITIES_REQUEST_EXPIRATION }).get(wmsRequesturl, { responseType: 'text' })
      .pipe(map(res => new WMSCapabilities().read(res)));
  }

  private findLayerByName(name: string, layerList: InternalWMSLayer[]): InternalWMSLayer {
    let layer = layerList.find(e => e.Name === name);
    if (layer) {
      return layer;
    } else {
      for (let i = 0; i < layerList.length; i++) {
        if (layerList[i].Layer) {
          layer = this.findLayerByName(name, layerList[i].Layer);
          if (layer) {
            return layer;
          }
        }
      }
    }
  }

  private getLayerInfo(layerName, url): Observable<InternalWMSLayer> {
    return this.getCapabilities(url).pipe(
      map(caps => this.findLayerByName(layerName, caps.Capability.Layer.Layer))
    );
  }

  private fixExtent(crs: string, extent: number[]): { crs: string, extent: number[] } {
    if (crs === 'EPSG:4326') {
      const fixExtent = [extent[1], extent[0], extent[3], extent[2]];
      return { crs, extent: fixExtent };
    } else {
      return { crs, extent };
    }
  }

  private createTimeList(timeDimension: { name: string; default: string; values: string; }): Date[] {
    return timeDimension.values.split(',').map(e => new Date(e));
  }

  private findNearestTimestamp(timeList: Date[], stamp: Date): Date {
    let bestDate = timeList.length;
    let bestDiff = -(new Date(0, 0, 0)).valueOf();
    let currDiff = 0;
    for (let i = 0; i < timeList.length; ++i) {
      currDiff = Math.abs(timeList[i].valueOf() - stamp.valueOf());
      if (currDiff < bestDiff) {
        bestDate = i;
        bestDiff = currDiff;
      }
    }
    return timeList[bestDate];
  }
}

result-matching ""

    No results matching ""