import { PoiStackMode , PoiTableDef , PoiTableEntryDef , PoiTableRef } from "@here/harp-datasource-protocol" ; import { LoggerManager } from "@here/harp-utils" ; import { MapView } from "../MapView" ; const logger = LoggerManager . instance . create ( "PoiTable" ) ; /**
 * Class to store and maintain individual POI information for the  PoiTable .
  class PoiTableEntry implements PoiTableEntryDef { param jsonEntry returns static verifyJSON ( jsonEntry ) { let isOK = typeof jsonEntry . name === "string" && jsonEntry . name . length > 0 && ( jsonEntry . altNames === undefined || Array . isArray ( jsonEntry . altNames ) ) && ( jsonEntry . stackMode === undefined || jsonEntry . stackMode === "yes" || jsonEntry . stackMode === "no" || jsonEntry . stackMode === "parent" ) && ( jsonEntry . visible === undefined || typeof jsonEntry . visible === "boolean" ) && ( jsonEntry . priority === undefined || typeof jsonEntry . priority === "number" ) && ( jsonEntry . iconMinLevel === undefined || typeof jsonEntry . iconMinLevel === "number" ) && ( jsonEntry . iconMaxLevel === undefined || typeof jsonEntry . iconMaxLevel === "number" ) && ( jsonEntry . textMinLevel === undefined || typeof jsonEntry . textMinLevel === "number" ) && ( jsonEntry . textMaxLevel === undefined || typeof jsonEntry . textMaxLevel === "number" ) ; if ( isOK && jsonEntry . altNames !== undefined ) { const altNames = jsonEntry . altNames as string [ ] ; for ( const str in altNames ) { if ( typeof str !== "string" ) { isOK = false ; break ; } } } return isOK ; } /** Default name of the POI as the key for looking it up. */ name ? : string ; /** Alternative names of the POI. */ altNames ? : string [ ] ; /** Visibility of the POI. If `false`, the POI will not be rendered. */ visible ? : boolean ; /** Name of the icon, defined in the the texture atlases. */ iconName ? : string ; /** Stacking mode of the POI. For future use. */ stackMode ? : PoiStackMode ; /**
     * Priority of the POI to select the visible set in case there are more POIs than can be
     * handled.
     */ priority ? : number ; /** Minimum zoom level to render the icon on. */ iconMinLevel ? : number ; /** Maximum zoom level to render the icon on. */ iconMaxLevel ? : number ; /** Minimum zoom level to render the text label on. */ textMinLevel ? : number ; /** Maximum zoom level to render the text label on. */ textMaxLevel ? : number ; param jsonEntry setup ( jsonEntry ) { this . name = jsonEntry . name ; this . altNames = jsonEntry . altNames ; this . iconName = jsonEntry . iconName ; this . visible = jsonEntry . visible ; this . priority = jsonEntry . priority ; this . iconMinLevel = jsonEntry . iconMinLevel ; this . iconMaxLevel = jsonEntry . iconMaxLevel ; this . textMinLevel = jsonEntry . textMinLevel ; this . textMaxLevel = jsonEntry . textMaxLevel ; switch ( jsonEntry . stackMode ) { case "yes" : this . stackMode = PoiStackMode . Show ; break ; case "no" : this . stackMode = PoiStackMode . Hide ; break ; case "parent" : this . stackMode = PoiStackMode . ShowParent ; break ; default : } } } remarks If a  TextElement has a
 * reference to a PoiTable (if TextElement.poiInfo.poiTableName is set), information for the
 * TextElement and its icon are read from the PoiTable.
 *
 * The key to look up the POI is taken from the data, in case of OSM data with TileZen data, the
 * `poiNameField` is set to `kind`, which makes the content of the field `kind` in the data the key
 * to look up the POIs in the  PoiTable .
 *
 * On the side of the  PoiTable , the key to look up the PoiTableEntry is either the property
 * "name" of the [[PoiTableEntry]] (which should be unique), or the alternative list of names
 * `altNames`, where each value should also be unique. If the property `useAltNamesForKey` is set to
 * `true`, the `altNames` will be used.
  export class PoiTable { /**
     * Stores the list of [[PoiTableEntry]]s.
     */ private readonly poiList : PoiTableEntry [ ] = new Array ( ) ; /**
     * Dictionary to look up for [[PoiTableEntry]] quickly. The dictionary is either created for
     * the `name` property of the [[PoiTableEntry]], which will identify POI, or for all of
     * alternative the names defined in `altNames` of [[PoiTableEntry]] JSON object.
     * Value assigned to key it is the index to [[poiList]] array which contain actual
     * [[PoiTabelEntry]] objects.
     */ private readonly poiDict : Map < string , number > = new Map ( ) ; private m_isLoading = false ; private m_loadedOk : boolean | undefined = undefined ; param { string } name param { boolean } useAltNamesForKey constructor ( readonly name , readonly useAltNamesForKey ) { } readonly get isLoading ( ) : boolean { return this . m_isLoading ; } readonly get loadedOk ( ) : boolean { return this . m_loadedOk === true ; } param poiName returns getEntry ( poiName ) { const entryIdx = this . poiDict . get ( poiName ) ; if ( entryIdx !== undefined ) { if ( entryIdx < this . poiList . length ) { return this . poiList [ entryIdx ] ; } else { throw new Error ( "Poi table entry index out of stored list!" ) ; } } return undefined ; } param { string } poiTableUrl param { AbortSignal } abortSignal returns { Promise < boolean > } async load ( poiTableUrl , abortSignal ? ) { if ( this . m_loadedOk !== undefined ) { return true ; } this . m_loadedOk = false ; const response = await fetch ( poiTableUrl , { signal : abortSignal } ) ; if ( ! response . ok ) { throw new Error ( `load: Cannot load POI table at ${ poiTableUrl }:` + ` ${ response . statusText }` ) ; } const jsonPoiTable = ( await response . json ( ) ) as PoiTableDef ; if ( jsonPoiTable === undefined ) { logger . info ( `load: TextureAtlas empty: ${ poiTableUrl }` ) ; return true ; } this . startLoading ( ) ; try { logger . debug ( `load: Loading POI table '${ poiTableUrl }' for table '${ this . name }'` ) ; if ( jsonPoiTable . poiList !== undefined && Array . isArray ( jsonPoiTable . poiList ) ) { for ( const tableEntry of jsonPoiTable . poiList ) { if ( PoiTableEntry . verifyJSON ( tableEntry ) ) { const newPoiEntry = new PoiTableEntry ( ) ; newPoiEntry . setup ( tableEntry ) ; const entryIdx = this . poiList . push ( newPoiEntry ) - 1 ; if ( ! this . useAltNamesForKey ) { if ( newPoiEntry . name === undefined ) { logger . warn ( `load: Invalid entry in POI table '${ poiTableUrl }' : ` + `. No name set in entry: ${ tableEntry }.` ) ; } else { this . poiDict . set ( newPoiEntry . name , entryIdx ) ; } } else { if ( newPoiEntry . altNames !== undefined && newPoiEntry . altNames . length > 0 ) { for ( const altName of newPoiEntry . altNames ) { this . poiDict . set ( altName , entryIdx ) ; } } else { logger . debug ( `load: Invalid entry in POI table '${ poiTableUrl }' : ` + `No alternative names set in entry: ${ JSON . stringify ( tableEntry ) }.` ) ; } } } else { logger . warn ( `load: Invalid entry in POI table '${ poiTableUrl }' : ${ JSON . stringify ( tableEntry ) }` ) ; } } } this . m_loadedOk = true ; this . finishedLoading ( ) ; } catch ( ex ) { logger . error ( `load: Failed to load POI table ` + `'${ poiTableUrl }' : ${ ex }` ) ; this . m_loadedOk = false ; this . finishedLoading ( ) ; return false ; } return true ; } private startLoading ( ) { this . m_isLoading = true ; } private finishedLoading ( ) { this . m_isLoading = false ; } } /**
 * The `PoiTableManager` manages the list of [[PoiTables]] that
 * can be defined in the  {@link @here/harp-datasource-protocol#Theme} sfile.
  export class PoiTableManager { private m_isLoading = false ; private m_poiTables : Map < string , PoiTable > = new Map ( ) ; private readonly m_abortControllers : Map < string , AbortController > = new Map ( ) ; param { MapView } mapView Owning  MapView .
      constructor ( readonly mapView ) { } /**
     * Load the  PoiTable s that are stored in the  MapView s
     *  {@link @here/harp-datasource-protocol#Theme} .
     *
     *  remarks Note that duplicate names of  PoiTable s in the
     *  {@link @here/harp-datasource-protocol#Theme} will lead to inaccessible  PoiTable s.
     *
     *  param poiTables -  {@link @here/harp-datasource-protocol#PoiTableRef[]} *                containing all  PoiTable s to load.
     *
     *  returns Resolved once all the  PoiTable s in
     *          the  {@link @here/harp-datasource-protocol#Theme} have been loaded.
      async loadPoiTables ( poiTables ? ) { const finished = new Promise < void > ( resolve => { this . clear ( ) ; if ( poiTables !== undefined ) { this . startLoading ( ) ; const loadPromises = new Array ( ) ; poiTables . forEach ( ( poiTableRef ) => { if ( poiTableRef !== undefined && poiTableRef . name !== undefined && typeof poiTableRef . name === "string" ) { const poiTable = new PoiTable ( poiTableRef . name , poiTableRef . useAltNamesForKey !== false ) ; if ( poiTableRef . url !== undefined && typeof poiTableRef . url === "string" ) { this . addTable ( poiTable ) ; this . m_abortControllers . set ( poiTableRef . name , new AbortController ( ) ) ; loadPromises . push ( poiTable . load ( poiTableRef . url , this . m_abortControllers . get ( poiTableRef . name ) ?. signal ) ) ; } else { logger . error ( `POI table definition has no valid url: ${ poiTableRef }` ) ; } } else { logger . error ( `POI table definition has no valid name: ${ poiTableRef }` ) ; } } ) ; if ( loadPromises . length > 0 ) { Promise . all ( loadPromises ) . finally ( ( ) => { this . finishLoading ( ) ; resolve ( ) ; } ) ; } else { this . finishLoading ( ) ; resolve ( ) ; } } else { this . finishLoading ( ) ; resolve ( ) ; } } ) ; return finished ; } /**
     * Clear the list of  PoiTable s.
      clear ( ) { this . m_poiTables = new Map ( ) ; this . m_abortControllers . forEach ( ( abortController , name ) => { abortController . abort ( ) ; this . m_abortControllers . delete ( name ) ; } ) ; } /**
     * Return the map of  PoiTable s.
      get poiTables ( ) : Map < string , PoiTable > { return this . m_poiTables ; } /**
     * Manually add a  PoiTable . Normally, the [[PoiTables]]s
     * are specified in the  {@link @here/harp-datasource-protocol#Theme} .
     *
     *  remarks addTable ( poiTable ) { this . m_poiTables . set ( poiTable . name , poiTable ) ; } /**
     * Retrieve a  PoiTable by name.
     *
     *  param { ( string | undefined ) } poiTableName Name of the  PoiTable .
     *
     *  returns { ( PoiTable | undefined ) } getPoiTable ( poiTableName ) { return poiTableName === undefined ? undefined : this . m_poiTables . get ( poiTableName ) ; } /**
     * Return `true` if the  PoiTable s have finished loading.
     *
     *  readonly get finishedLoading ( ) : boolean { return ! this . m_isLoading ; } private startLoading ( ) { this . m_isLoading = true ; } private finishLoading ( ) { this . m_isLoading = false ; } }