UNPKG

@here/harp-mapview

Version:

Functionality needed to render a map.

307 lines 14.4 kB
"use strict"; /* * Copyright (C) 2019-2021 HERE Europe B.V. * Licensed under Apache 2.0, see full license in LICENSE * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PoiManager = void 0; const harp_datasource_protocol_1 = require("@here/harp-datasource-protocol"); const harp_utils_1 = require("@here/harp-utils"); const THREE = require("three"); const TextElementBuilder_1 = require("../text/TextElementBuilder"); const logger = harp_utils_1.LoggerManager.instance.create("PoiManager"); function getImageTexture(poiGeometry, index = 0) { if (poiGeometry.imageTextures) { const textureNameIndex = poiGeometry.imageTextures[index]; if (textureNameIndex >= 0) { harp_utils_1.assert(poiGeometry.imageTextures.length > index); return poiGeometry.stringCatalog[textureNameIndex]; } } return undefined; } function getAttributes(poiGeometry, index = 0) { return poiGeometry.objInfos ? poiGeometry.objInfos[index] : undefined; } function getPosition(positionAttribute, worldOffsetX, index = 0) { const position = new THREE.Vector3().fromBufferAttribute(positionAttribute, index); position.x += worldOffsetX; return position; } function getText(poiGeometry, index = 0) { var _a; harp_utils_1.assert(poiGeometry.texts.length > index); const stringIndex = poiGeometry.texts[index]; harp_utils_1.assert(poiGeometry.stringCatalog.length > stringIndex); return (_a = poiGeometry.stringCatalog[stringIndex]) !== null && _a !== void 0 ? _a : ""; } /** * POI manager class, responsible for loading the * {@link @here/harp-datasource-protocol#PoiGeometry} objects * from the {@link @here/harp-datasource-protocol#DecodedTile}, * and preparing them for rendering. * * @remarks * Also loads and manages the texture atlases for the icons. */ class PoiManager { /** * The constructor of the `PoiManager`. * * @param mapView - The {@link MapView} instance that should display the POIs. */ constructor(mapView) { this.mapView = mapView; this.m_imageTextures = new Map(); this.m_poiShieldGroups = new Map(); } /** * Warn about a missing POI table name, but only once. * @param poiTableName - POI mapping table name. * @param poiTable - POI table instance. */ static notifyMissingPoiTable(poiTableName, poiTable) { if (poiTableName === undefined) { poiTableName = "undefined"; } if (PoiManager.m_missingPoiTableName.get(poiTableName) === undefined) { PoiManager.m_missingPoiTableName.set(poiTableName, true); if (poiTable !== undefined && !poiTable.loadedOk) { logger.error(`updatePoiFromPoiTable: Could not load POI table '${poiTableName}'!`); } else { logger.error(`updatePoiFromPoiTable: No POI table with name '${poiTableName}' found!`); } } } /** * Warn about a missing POI name, but only once. * @param poiName - name of POI. * @param poiTableName - POI mapping table name. */ static notifyMissingPoi(poiName, poiTableName) { if (poiName === undefined) { poiName = "undefined"; } const key = `${poiTableName}[${poiName}]`; if (PoiManager.m_missingPoiName.get(key) === undefined) { PoiManager.m_missingPoiName.set(key, true); logger.warn(`updatePoiFromPoiTable: ` + `Cannot find POI info for '${poiName}' in table '${poiTableName}'.`); } } /** * Add all POIs from a decoded tile and store them as {@link TextElement}s in the {@link Tile}. * * Also handles LineMarkers, which is a recurring marker along a line (road). * * @param tile - Tile to add POIs to. * @param decodedTile - DecodedTile containing the raw * {@link @here/harp-datasource-protocol#PoiGeometry} * objects describing the POIs. */ addPois(tile, decodedTile) { const poiGeometries = harp_utils_1.assertExists(decodedTile.poiGeometries); const worldOffsetX = tile.computeWorldOffsetX(); const mapView = tile.mapView; const discreteZoomLevel = Math.floor(mapView.zoomLevel); const intZoomEnv = new harp_datasource_protocol_1.MapEnv({ $zoom: discreteZoomLevel }, mapView.env); const poiBuilder = new TextElementBuilder_1.TextElementBuilder(intZoomEnv, tile.textStyleCache, tile.dataSource.dataSourceOrder); for (const poiGeometry of poiGeometries) { harp_utils_1.assert(poiGeometry.technique !== undefined); const techniqueIndex = harp_utils_1.assertExists(poiGeometry.technique); const technique = decodedTile.techniques[techniqueIndex]; if (technique._kindState === false || (!harp_datasource_protocol_1.isLineMarkerTechnique(technique) && !harp_datasource_protocol_1.isPoiTechnique(technique))) { continue; } if (technique.showOnMap === false) { continue; } const positions = new THREE.BufferAttribute(new Float64Array(poiGeometry.positions.buffer), poiGeometry.positions.itemCount); poiBuilder.withTechnique(technique); if (harp_datasource_protocol_1.isLineMarkerTechnique(technique) && positions.count > 0) { this.addLineMarker(poiBuilder, tile, poiGeometry, positions, worldOffsetX); } else if (harp_datasource_protocol_1.isPoiTechnique(technique)) { this.addPoi(poiBuilder, tile, poiGeometry, positions, worldOffsetX); } } } /** * Load the texture atlas that defines the segments of the texture that should be used for * specific icons. * * @remarks * Creates an {@link @here/harp-datasource-protocol#ImageTexture} * for every element in the atlas, such that it can * be addressed in the theme file. * * @param imageName - Name of the image from the theme (NOT the url!). * @param atlas - URL of the JSON file defining the texture atlas. * @param abortSignal - Signal to Abort the loading of the Atlas Image */ async addTextureAtlas(imageName, atlas, abortSignal) { const response = await fetch(atlas, { signal: abortSignal }); if (!response.ok) { throw new Error(`addTextureAtlas: Cannot load textureAtlas: ${response.statusText}`); } try { const jsonAtlas = await response.json(); if (jsonAtlas === undefined) { logger.info(`addTextureAtlas: TextureAtlas empty: ${atlas}`); return; } logger.debug(`addTextureAtlas: Loading textureAtlas '${atlas}' for image '${imageName}'`); for (const textureName of Object.getOwnPropertyNames(jsonAtlas)) { const imageTextureDef = jsonAtlas[textureName]; const imageTexture = { name: textureName, image: imageName, xOffset: imageTextureDef.x, yOffset: imageTextureDef.y, width: imageTextureDef.width, height: imageTextureDef.height }; this.addImageTexture(imageTexture); } this.mapView.update(); } catch (error) { logger.error(`addTextureAtlas: Failed to load textureAtlas '${atlas}' : ${error}`); } } /** * Add an {@link @here/harp-datasource-protocol#ImageTexture} such that it * is available as a named entity for techniques in theme files. * * @param imageTexture - {@link @here/harp-datasource-protocol#ImageTexture} * that should be available for POIs. */ addImageTexture(imageTexture) { if (imageTexture.name === undefined) { logger.error("addImageTexture: Name required", imageTexture); return; } if (this.m_imageTextures.get(imageTexture.name) !== undefined) { logger.warn(`addImageTexture: Name already used: ${imageTexture.name}` + ` (overriding it)`); } this.m_imageTextures.set(imageTexture.name, imageTexture); } /** * Return the {@link @here/harp-datasource-protocol#ImageTexture} * registered under the specified name. * * @param name - Name of the {@link @here/harp-datasource-protocol#ImageTexture}. */ getImageTexture(name) { return this.m_imageTextures.get(name); } /** * Update the {@link TextElement} with the information taken from the {@link PoiTable} which is * referenced in the {@link PoiInfo} of the pointLabel. * * If the requested {@link PoiTable} is not available yet, the function returns `false`. * If the {@link PoiTable} is not defined, or if the references POI has no entry in * the {@link PoiTable}, no action is taken, and the function returns `false`. * * If the {@link PoiTable} has been processed, it returns `true`, indicating that this function * doesn't have to be called again. * * @param pointLabel - The {@link TextElement} to update. * * @returns `true` if the {@link PoiTable} has been processed, and the * function does not have to be called again. */ updatePoiFromPoiTable(pointLabel) { var _a, _b, _c, _d, _e, _f; const poiInfo = pointLabel.poiInfo; // PoiTable requires poiName to be defined otherwise mapping via PoiTable is // not possible, such as table key is not defined. if (!poiInfo || poiInfo.poiTableName === undefined || poiInfo.poiName === undefined) { return true; } // Try to acquire PoiTable const poiTableName = poiInfo.poiTableName; const poiTable = this.mapView.poiTableManager.getPoiTable(poiTableName); // Check if PoiTable is found, but its still loading. if (poiTable && poiTable.isLoading) { // The PoiTable is still loading, we have to try again. return false; } // Remove poiTableName to mark this POI as processed. poiInfo.poiTableName = undefined; // PoiTable not found or can not be loaded. if (!poiTable || !poiTable.loadedOk) { PoiManager.notifyMissingPoiTable(poiTableName, poiTable); return true; } // Try to acquire PoiTableEntry. const poiName = poiInfo.poiName; const poiTableEntry = poiTable.getEntry(poiName); if (!poiTableEntry) { PoiManager.notifyMissingPoi(poiName, poiTableName); return true; } if (poiTableEntry.iconName !== undefined && poiTableEntry.iconName.length > 0) { poiInfo.imageTextureName = harp_datasource_protocol_1.composeTechniqueTextureName(poiTableEntry.iconName, poiInfo.technique); } pointLabel.visible = (_a = poiTableEntry.visible) !== null && _a !== void 0 ? _a : pointLabel.visible; pointLabel.priority = (_b = poiTableEntry.priority) !== null && _b !== void 0 ? _b : pointLabel.priority; poiInfo.iconMinZoomLevel = (_c = poiTableEntry.iconMinLevel) !== null && _c !== void 0 ? _c : poiInfo.iconMinZoomLevel; poiInfo.iconMaxZoomLevel = (_d = poiTableEntry.iconMaxLevel) !== null && _d !== void 0 ? _d : poiInfo.iconMaxZoomLevel; poiInfo.textMinZoomLevel = (_e = poiTableEntry.textMinLevel) !== null && _e !== void 0 ? _e : poiInfo.textMinZoomLevel; poiInfo.textMaxZoomLevel = (_f = poiTableEntry.textMaxLevel) !== null && _f !== void 0 ? _f : poiInfo.textMaxZoomLevel; TextElementBuilder_1.TextElementBuilder.alignZoomLevelRanges(pointLabel); return true; } /** * Clear internal state. Applicable when switching themes. */ clear() { this.m_imageTextures.clear(); this.m_poiShieldGroups.clear(); } /** * Add the LineMarker as a POI with multiple positions sharing the same `shieldGroupIndex`. */ addLineMarker(poiBuilder, tile, poiGeometry, positions, worldOffsetX) { const text = getText(poiGeometry); const imageTextureName = getImageTexture(poiGeometry); // let the combined image texture name (name of image in atlas, not the URL) and // text of the shield be the group key, at worst scenario it may be: "undefined-" const groupKey = imageTextureName + "-" + text; let shieldGroupIndex = this.m_poiShieldGroups.get(groupKey); if (shieldGroupIndex === undefined) { shieldGroupIndex = this.m_poiShieldGroups.size; this.m_poiShieldGroups.set(groupKey, shieldGroupIndex); } const positionArray = []; for (let i = 0; i < positions.count; i += 3) { positionArray.push(getPosition(positions, worldOffsetX, i)); } const textElement = poiBuilder .withIcon(imageTextureName, shieldGroupIndex) .build(text, positionArray, tile.offset, tile.dataSource.name, tile.dataSource.dataSourceOrder, getAttributes(poiGeometry)); tile.addTextElement(textElement); } /** * Create and add POI {@link TextElement}s to tile with a series of positions. */ addPoi(poiBuilder, tile, poiGeometry, positions, worldOffsetX) { var _a, _b; for (let i = 0; i < positions.count; ++i) { const offsetDirection = (_b = (_a = poiGeometry.offsetDirections) === null || _a === void 0 ? void 0 : _a[i]) !== null && _b !== void 0 ? _b : 0; const textElement = poiBuilder .withIcon(getImageTexture(poiGeometry, i)) .build(getText(poiGeometry, i), getPosition(positions, worldOffsetX, i), tile.offset, tile.dataSource.name, tile.dataSource.dataSourceOrder, getAttributes(poiGeometry, i), undefined, offsetDirection); tile.addTextElement(textElement); } } } exports.PoiManager = PoiManager; // Keep track of the missing POI table names, but only warn once. PoiManager.m_missingPoiTableName = new Map(); PoiManager.m_missingPoiName = new Map(); //# sourceMappingURL=PoiManager.js.map