import "@here/harp-fetch" ; import { TileKey , TilingScheme } from "@here/harp-geoutils" ; import { DataProvider } from "@here/harp-mapview-decoder" ; import { ITransferManager , TransferManager } from "@here/harp-transfer-manager" ; import { LoggerManager } from "@here/harp-utils" ; const logger = LoggerManager . instance . create ( "OmvRestClient" ) ; interface QueryParameters { [ key ] : string ; } export enum APIFormat { remarks HereV1 , remarks MapboxV4 , remarks XYZMVT , remarks XYZJson , remarks XYZOMV , remarks TomtomV1 , remarks XYZSpace } /**
 * Authentication token/code provider used by [[OmvRestClient]] before each call to currently valid
 * authentication code/token.
 */ export type AuthenticationCodeProvider = ( ) => Promise < string > ; export enum AuthenticationMethod { QueryString , AuthorizationHeader } export interface AuthenticationMethodInfo { method : AuthenticationMethod ; name ? : string ; } /**
 * Authentication method, where token will be provided as HTTP Header:
 *
 *    Authorization: Bearer $authenticationToken
 */ export const AuthenticationTypeBearer = { method : AuthenticationMethod . AuthorizationHeader , name : "Bearer" } ; /**
 * TomTomV1 API compatible authorization method, where token will be provided as HTTP Header:
 *
 *    Authorization: Bearer $authenticationToken
 */ export const AuthenticationTypeTomTomV1 = { method : AuthenticationMethod . QueryString , name : "key" } ; export const AuthenticationTypeAccessToken = { method : AuthenticationMethod . QueryString , name : "access_token" } ; export interface OmvRestClientParameters { remarks `URL` with special keywords replaced to retrieve specific tile:
     *  - `{z}` - zoom level of tile, @see  {@link @here/harp-geoutils#TileKey.level} *  - `{x}` - horizontal coordinate of tile (column number),
     *            see  {@link @here/harp-geoutils#TileKey.column} *  - `{y}` - vertical coordinate of Tile (row number),
     *            see  {@link @here/harp-geoutils#TileKey.row} *
     * Examples of `url` patterns:
     * ```
     *   https://my-base-url.com/vector-tiles/{z}/{x}/{y}.mvt
     *   https://xyz.api.here.com/tiles/herebase.02/{z}/{x}/{y}/omv
     *   https://xyz.api.here.com/tiles/osmbase/512/all/{z}/{x}/{y}.mvt
     * ```
     *
     * Note: To add authentication headers and/or query params, use [[authMethod]], [[urlParams]]
     * properties or embed token directly in `url`.
     *
     * Complete examples:
     * ```
     * // XYZ OSM with authentication using query param
     * {
     *     url: "https://xyz.api.here.com/tiles/osmbase/512/all/{z}/{x}/{y}.mvt",
     *     urlParams: {
     *           access_token: accessToken
     *     },
     * }
     * // HERE Vector Tile with authentication using bearer token retrieved by callback
     * {
     *     url: "https://vector.hereapi.com/v2/vectortiles/base/mc/{z}/{x}/{y}/omv",
     *     authenticationMethod: AuthenticationTypeBearer,
     *     authenticationCode: () => getBearerToken()
     * }
     * ```
      url ? : string ; see baseUrl ? : string ; remarks see authenticationCode ? : string | AuthenticationCodeProvider ; remarks authenticationMethod ? : AuthenticationMethodInfo ; /**
     * The REST API format to use for the tile path generation, will default to the HERE Vector Tile
     * API.
     */ apiFormat ? : APIFormat ; /**
     * Tiling scheme is used in some of the APIs, not implemented yet.
     */ tilingScheme ? : TilingScheme ; /**
     * Transfer Manager to use; creates an own instance if none passed.
     */ downloadManager ? : ITransferManager ; deprecated getBearerToken ? : ( ) => Promise < string > ; /**
     * Array of query parameters to be appended at the end of the url.
     * It is empty by default.
     */ urlParams ? : { [ key ] : string ; } ; } /**
 * REST client supporting getting protobuf OMV Tile from REST-based servers.
 */ export class OmvRestClient extends DataProvider { private readonly downloadManager : ITransferManager ; private readonly urlParams : { [ key ] : string ; } ; constructor ( readonly params ) { super ( ) ; this . downloadManager = params . downloadManager === undefined ? TransferManager . instance ( ) : params . downloadManager ; this . urlParams = params . urlParams === undefined ? { } : params . urlParams ; } /** Overriding abstract method, in this case doing nothing. */ async connect ( ) { } /** Overriding abstract method, in this case always returning `true`. */ ready ( ) { return true ; } remarks example param tileKey param tileRequestInit returns async getTile ( tileKey , abortSignal ? ) { const init = { signal : abortSignal } ; let tileUrl = this . dataUrl ( tileKey ) ; const authenticationCode = await this . getActualAuthenticationCode ( ) ; tileUrl = this . applyAuthCode ( tileUrl , init , authenticationCode ) ; tileUrl = this . addQueryParams ( tileUrl , this . urlParams ) ; if ( this . params . apiFormat === APIFormat . XYZJson ) { return await this . downloadManager . downloadJson ( tileUrl , init ) ; } return await this . downloadManager . downloadArrayBuffer ( tileUrl , init ) ; } /**
     * Destroys this `OmvRestClient`.
     */ dispose ( ) { } /**
     * Get actual authentication code/token for this request according to configuration.
     */ private async getActualAuthenticationCode ( ) { if ( typeof this . params . authenticationCode === "string" ) { return this . params . authenticationCode ; } else if ( this . params . authenticationCode !== undefined ) { return await this . params . authenticationCode ( ) ; } else if ( this . params . getBearerToken !== undefined ) { return await this . params . getBearerToken ( ) ; } else { return undefined ; } } /**
     * Get default authentication method basing on apiFormat and other params.
     */ private getDefaultAuthMethod ( ) { if ( this . params . getBearerToken !== undefined ) { return AuthenticationTypeBearer ; } switch ( this . params . apiFormat ) { case APIFormat . HereV1 : return AuthenticationTypeBearer ; case APIFormat . MapboxV4 : case APIFormat . XYZOMV : case APIFormat . XYZMVT : case APIFormat . XYZSpace : case APIFormat . XYZJson : return AuthenticationTypeAccessToken ; case APIFormat . TomtomV1 : return AuthenticationTypeTomTomV1 ; default : logger . warn ( `#getDefaultAuthMethod: Not supported API format: ${ this . params . apiFormat }` ) ; return undefined ; } } param url param init param authenticationCode return private applyAuthCode ( url , init , authenticationCode ) { if ( authenticationCode === undefined ) { return url ; } const authMethod = this . params . authenticationMethod ?? this . getDefaultAuthMethod ( ) ; if ( authMethod === undefined ) { return url ; } if ( authMethod . method === AuthenticationMethod . AuthorizationHeader ) { if ( init . headers === undefined ) { init . headers = new Headers ( ) ; } const authType = authMethod . name ?? "Bearer" ; ( init . headers as Headers ) . append ( "Authorization" , `${ authType } ${ authenticationCode }` ) ; } else if ( authMethod . method === AuthenticationMethod . QueryString ) { const attrName = authMethod . name ?? "access_token" ; const authParams = { } ; authParams [ attrName ] = authenticationCode ; url = this . addQueryParams ( url , authParams ) ; } return url ; } /**
     * Get actual tile URL depending on configured API format.
     */ private dataUrl ( tileKey ) { if ( this . params . url !== undefined ) { return this . params . url . replace ( "{x}" , String ( tileKey . column ) ) . replace ( "{y}" , String ( tileKey . row ) ) . replace ( "{z}" , String ( tileKey . level ) ) ; } let path = [ `/${ tileKey . level }` , tileKey . column , tileKey . row ] . join ( this . params . apiFormat === APIFormat . XYZSpace || this . params . apiFormat === APIFormat . XYZJson ? "_" : "/" ) ; switch ( this . params . apiFormat ) { case APIFormat . HereV1 : case APIFormat . XYZOMV : path += "/omv" ; break ; case APIFormat . MapboxV4 : path += ".mvt" ; break ; case APIFormat . XYZMVT : path += ".mvt" ; break ; case APIFormat . XYZJson : break ; case APIFormat . XYZSpace : path += ".mvt" ; break ; case APIFormat . TomtomV1 : path += ".pbf" ; break ; default : logger . warn ( `Not supported API format: ${ this . params . apiFormat }` ) ; break ; } return this . params . baseUrl + path ; } private addQueryParams ( url , queryParams ) { let queryString = "" ; let sep = url . includes ( "?" ) ? "&" : "?" ; for ( const prop in queryParams ) { if ( ! queryParams . hasOwnProperty ( prop ) ) { continue ; } queryString += `${ sep }${ encodeURIComponent ( prop ) }=${ encodeURIComponent ( queryParams [ prop ] ) }` ; if ( sep === "?" ) { sep = "&" ; } } return url + queryString ; } }