UNPKG

@here/harp-mapview

Version:

Functionality needed to render a map.

929 lines 34.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tile = exports.getFeatureDataSize = void 0; const harp_geoutils_1 = require("@here/harp-geoutils"); const harp_utils_1 = require("@here/harp-utils"); const THREE = require("three"); const LodMesh_1 = require("./geometry/LodMesh"); const Object3DUtils_1 = require("./geometry/Object3DUtils"); const TileGeometryLoader_1 = require("./geometry/TileGeometryLoader"); const ITileLoader_1 = require("./ITileLoader"); const Statistics_1 = require("./Statistics"); const TextElement_1 = require("./text/TextElement"); const TextElementGroup_1 = require("./text/TextElementGroup"); const TextElementGroupPriorityList_1 = require("./text/TextElementGroupPriorityList"); const TileTextStyleCache_1 = require("./text/TileTextStyleCache"); const logger = harp_utils_1.LoggerManager.instance.create("Tile"); /** * Minimum estimated size of a JS object. */ const MINIMUM_SMALL_OBJECT_SIZE_ESTIMATION = 16; const MINIMUM_OBJECT_SIZE_ESTIMATION = 100; /** * Compute the memory footprint of `TileFeatureData`. * * @internal */ function getFeatureDataSize(featureData) { let numBytes = MINIMUM_OBJECT_SIZE_ESTIMATION; if (featureData.starts !== undefined) { numBytes += featureData.starts.length * 8; } if (featureData.objInfos !== undefined) { // 16 (estimated) bytes per objInfos numBytes += featureData.objInfos.length * MINIMUM_SMALL_OBJECT_SIZE_ESTIMATION; } return numBytes; } exports.getFeatureDataSize = getFeatureDataSize; /** * The class that holds the tiled data for a {@link DataSource}. */ class Tile { /** * Creates a new {@link Tile}. * * @param dataSource - The {@link DataSource} that created this {@link Tile}. * @param tileKey - The unique identifier for this {@link Tile}. * Currently only up to level 24 is * supported, because of the use of the upper bits for the offset. * @param offset - The optional offset, this is an integer which represents what multiple of 360 * degrees to shift, only useful for flat projections, hence optional. * @param localTangentSpace - Whether the tile geometry is in local tangent space or not. */ constructor(dataSource, tileKey, offset = 0, localTangentSpace) { this.dataSource = dataSource; this.tileKey = tileKey; /** * A list of the THREE.js objects stored in this `Tile`. */ this.objects = []; /** * The optional list of HERE TileKeys of tiles with geometries that cross the boundaries of this * `Tile`. */ this.dependencies = []; /** * Keeping some stats for the individual {@link Tile}s to analyze caching behavior. * * The frame the {@link Tile} was last requested. This is * required to know when the given {@link Tile} * can be removed from the cache. */ this.frameNumLastRequested = -1; /** * The frame the `Tile` was first visible. */ this.frameNumVisible = -1; /** * The last frame this `Tile` has been rendered (or was in the visible set). Used to determine * visibility of `Tile` at the end of a frame, if the number is the current frame number, it is * visible. */ this.frameNumLastVisible = -1; /** * After removing from cache, this is the number of frames the `Tile` was visible. */ this.numFramesVisible = 0; /** * Version stamp of the visibility set in the [[TileManager]]. If the counter is different, the * visibility of the Tile's objects has to be calculated. Optimization to reduce overhead of * computing visibility. */ this.visibilityCounter = -1; /** * @hidden * * Used to tell if the Tile is used temporarily as a fallback tile. * * levelOffset is in in the range [-quadTreeSearchDistanceUp, * quadTreeSearchDistanceDown], where these values come from the * {@link VisibleTileSetOptions} */ this.levelOffset = 0; /** * If the tile should not be rendered, this is used typically when the tile in question * is completely covered by another tile and therefore can be skipped without any visual * impact. Setting this value directly affects the [[willRender]] method, unless * overriden by deriving classes. */ this.skipRendering = false; /** * If the tile should not yet be rendered, this is used typically when the tile in question * does not fit into the gpu upload limit of the current frame. * Setting this value directly affects the [[willRender]] method, unless * overriden by deriving classes. */ this.delayRendering = false; /** * The bounding box of this `Tile` in world coordinates. */ this.m_boundingBox = new harp_geoutils_1.OrientedBox3(); this.m_disposed = false; this.m_forceHasGeometry = undefined; // Used for {@link TextElement}s that are stored in the data, and that are placed explicitly, // fading in and out. this.m_textElementGroups = new TextElementGroupPriorityList_1.TextElementGroupPriorityList(); // Blocks other labels from showing. this.m_pathBlockingElements = []; // Center of the tile's un-elevated bounding box world coordinates. this.m_worldCenter = new THREE.Vector3(); this.m_visibleArea = 0; // Tile elevation range in meters this.m_elevationRange = { minElevation: 0, maxElevation: 0 }; // List of owned textures for disposal this.m_ownedTextures = new WeakSet(); this.geoBox = this.dataSource.getTilingScheme().getGeoBox(this.tileKey); this.updateBoundingBox(); this.m_worldCenter.copy(this.boundingBox.position); this.m_localTangentSpace = localTangentSpace !== null && localTangentSpace !== void 0 ? localTangentSpace : false; this.m_textStyleCache = new TileTextStyleCache_1.TileTextStyleCache(this); this.m_offset = offset; this.m_uniqueKey = harp_geoutils_1.TileKeyUtils.getKeyForTileKeyAndOffset(this.tileKey, this.offset); if (dataSource.useGeometryLoader) { this.m_tileGeometryLoader = new TileGeometryLoader_1.TileGeometryLoader(this, this.mapView.taskQueue); this.attachGeometryLoadedCallback(); } } /** * The visibility status of the {@link Tile}. It is actually * visible or planned to become visible. */ get isVisible() { // Tiles are not evaluated as invisible until the second frame they aren't requested. // This happens in order to prevent that, during VisibleTileSet visibility evaluation, // visible tiles that haven't yet been evaluated for the current frame are preemptively // removed from [[DataSourceCache]]. // There is cases when a tile was already removed from the MapView, i.e. the PolaCaps // Datasource might get remove on a change of projection, in this case // this.dataSource.mapView will throw an error try { return this.frameNumLastRequested >= this.dataSource.mapView.frameNumber - 1; } catch (error) { logger.debug(error); return false; } } /** * Sets the tile visibility status. * @param visible - `True` to mark the tile as visible, `False` otherwise. */ set isVisible(visible) { this.frameNumLastRequested = visible ? this.dataSource.mapView.frameNumber : -1; if (!visible && this.m_tileGeometryLoader && !this.m_tileGeometryLoader.isSettled) { this.m_tileGeometryLoader.cancel(); } } /** * The {@link @here/harp-geoutils#Projection} currently used by the {@link MapView}. */ get projection() { return this.dataSource.projection; } /** * The {@link MapView} this `Tile` belongs to. */ get mapView() { return this.dataSource.mapView; } /** * Whether the data of this tile is in local tangent space or not. * * @remarks * If the data is in local tangent space (i.e. up vector is (0,0,1) for high zoomlevels) then * {@link MapView} will rotate the objects before rendering using the rotation matrix of the * oriented [[boundingBox]]. */ get localTangentSpace() { return this.m_localTangentSpace; } /* * The size of this Tile in system memory. */ get memoryUsage() { if (this.m_resourceInfo === undefined) { this.computeResourceInfo(); } return this.m_resourceInfo.heapSize; } /** * The center of this `Tile` in world coordinates. */ get center() { return this.m_worldCenter; } /** * Gets the key to uniquely represent this tile (based on * the {@link tileKey} and {@link offset}). * * @remarks * This key is only unique within the given {@link DataSource}, * to get a key which is unique across * {@link DataSource}s see [[DataSourceCache.getKeyForTile]]. */ get uniqueKey() { return this.m_uniqueKey; } /** * The optional offset, this is an integer which represents what multiple of 360 degrees to * shift, only useful for flat projections, hence optional. */ get offset() { return this.m_offset; } /** * The optional offset, this is an integer which represents what multiple of 360 degrees to * shift, only useful for flat projections, hence optional. * @param offset - Which multiple of 360 degrees to apply to the {@link Tile}. */ set offset(offset) { if (this.m_offset !== offset) { this.m_uniqueKey = harp_geoutils_1.TileKeyUtils.getKeyForTileKeyAndOffset(this.tileKey, offset); } this.m_offset = offset; } /** * Compute {@link TileResourceInfo} of this `Tile`. * * @remarks * May be using a cached value. The method * `invalidateResourceInfo` can be called beforehand to force a recalculation. * * @returns `TileResourceInfo` for this `Tile`. */ getResourceInfo() { if (this.m_resourceInfo === undefined) { this.computeResourceInfo(); } return this.m_resourceInfo; } /** * Force invalidation of the cached {@link TileResourceInfo}. * * @remarks * Useful after the `Tile` has been * modified. */ invalidateResourceInfo() { this.m_resourceInfo = undefined; } /** * Add ownership of a texture to this tile. * * @remarks * The texture will be disposed if the `Tile` is disposed. * @param texture - Texture to be owned by the `Tile` */ addOwnedTexture(texture) { this.m_ownedTextures.add(texture); } /** * @internal * @deprecated User text elements are deprecated. * * Gets the list of developer-defined {@link TextElement} in this `Tile`. * * @remarks * This list is always rendered first. */ get userTextElements() { let group = this.m_textElementGroups.groups.get(TextElement_1.TextElement.HIGHEST_PRIORITY); if (group === undefined) { group = new TextElementGroup_1.TextElementGroup(TextElement_1.TextElement.HIGHEST_PRIORITY); this.m_textElementGroups.groups.set(group.priority, group); } return group; } /** * Adds a developer-defined {@link TextElement} to this `Tile`. * * @remarks * The {@link TextElement} is always * visible, if it's in the map's currently visible area. * * @deprecated use [[addTextElement]]. * * @param textElement - The Text element to add. */ addUserTextElement(textElement) { textElement.priority = TextElement_1.TextElement.HIGHEST_PRIORITY; this.addTextElement(textElement); } /** * Removes a developer-defined {@link TextElement} from this `Tile`. * * @deprecated use `removeTextElement`. * * @param textElement - A developer-defined TextElement to remove. * @returns `true` if the element has been removed successfully; `false` otherwise. */ removeUserTextElement(textElement) { textElement.priority = TextElement_1.TextElement.HIGHEST_PRIORITY; return this.removeTextElement(textElement); } /** * Adds a {@link TextElement} to this `Tile`, which is added to the visible set of * {@link TextElement}s based on the capacity and visibility. * * @remarks * The {@link TextElement}'s priority controls if or when it becomes visible. * * To ensure that a TextElement is visible, use a high value for its priority, such as * `TextElement.HIGHEST_PRIORITY`. Since the number of visible TextElements is limited by the * screen space, not all TextElements are visible at all times. * * @param textElement - The TextElement to add. */ addTextElement(textElement) { this.textElementGroups.add(textElement); if (this.m_textElementsChanged === false) { // HARP-8733: Clone all groups so that they are handled as new element groups // by TextElementsRenderer and it doesn't try to reuse the same state stored // for the old groups. this.m_textElementGroups = this.textElementGroups.clone(); } this.textElementsChanged = true; } /** * Adds a `PathBlockingElement` to this `Tile`. * * @remarks * This path has the highest priority and blocks * all other labels. There maybe in future a use case to give it a priority, but as that isn't * yet required, it is left to be implemented later if required. * @param blockingElement - Element which should block all other labels. */ addBlockingElement(blockingElement) { this.m_pathBlockingElements.push(blockingElement); } /** * Removes a {@link TextElement} from this `Tile`. * * @remarks * For the element to be removed successfully, the * priority of the {@link TextElement} has to be equal to its priority when it was added. * * @param textElement - The TextElement to remove. * @returns `true` if the TextElement has been removed successfully; `false` otherwise. */ removeTextElement(textElement) { const groups = this.textElementGroups; if (!groups.remove(textElement)) { return false; } if (this.m_textElementsChanged === false) { // HARP-8733: Clone all groups so that they are handled as new element groups // by TextElementsRenderer and it doesn't try to reuse the same state stored // for the old groups. this.m_textElementGroups = groups.clone(); } this.textElementsChanged = true; return true; } /** * @internal * * Gets the current `GroupedPriorityList` which * contains a list of all {@link TextElement}s to be * selected and placed for rendering. */ get textElementGroups() { return this.m_textElementGroups; } /** * Gets the current modification state for the list * of {@link TextElement}s in the `Tile`. * * @remarks * If the value is `true` the `TextElement` is placed for * rendering during the next frame. */ get textElementsChanged() { var _a; return (_a = this.m_textElementsChanged) !== null && _a !== void 0 ? _a : false; } set textElementsChanged(changed) { this.m_textElementsChanged = changed; } /** * Returns true if the `Tile` has any text elements to render. */ hasTextElements() { return this.m_textElementGroups.count() > 0; } /** * Get the current blocking elements. */ get blockingElements() { return this.m_pathBlockingElements; } /** * Called before {@link MapView} starts rendering this `Tile`. * * @remarks * @param zoomLevel - The current zoom level. * @returns Returns `true` if this `Tile` should be rendered. Influenced directly by the * `skipRendering` property unless specifically overriden in deriving classes. */ willRender(_zoomLevel) { return !this.skipRendering && !this.delayRendering; } /** * Called after {@link MapView} has rendered this `Tile`. */ didRender() { // to be overridden by subclasses } /** * Estimated visible area of tile used for sorting the priorities during loading. */ get visibleArea() { return this.m_visibleArea; } set visibleArea(area) { this.m_visibleArea = area; if (this.tileLoader !== undefined) { this.tileLoader.priority = area; } } /** * @internal * Gets the tile's ground elevation range in meters. */ get elevationRange() { return this.m_elevationRange; } /** * @internal * Sets the tile's ground elevation range in meters. * * @param elevationRange - The elevation range. */ set elevationRange(elevationRange) { var _a; if (elevationRange.minElevation === this.m_elevationRange.minElevation && elevationRange.maxElevation === this.m_elevationRange.maxElevation && elevationRange.calculationStatus === this.m_elevationRange.calculationStatus) { return; } this.m_elevationRange.minElevation = elevationRange.minElevation; this.m_elevationRange.maxElevation = elevationRange.maxElevation; this.m_elevationRange.calculationStatus = elevationRange.calculationStatus; this.elevateGeoBox(); // Only update bounding box if tile has already been decoded and a maximum/minimum geometry // height is provided by the data source. if (this.m_maxGeometryHeight !== undefined || this.m_minGeometryHeight !== undefined) { harp_utils_1.assert(((_a = this.decodedTile) === null || _a === void 0 ? void 0 : _a.boundingBox) === undefined); this.updateBoundingBox(); } } /** * Gets the decoded tile; it is removed after geometry handling. */ get decodedTile() { return this.m_decodedTile; } /** * Applies the decoded tile to the tile. * * @remarks * If the geometry is empty, then the tile's forceHasGeometry flag is set. * Map is updated. * @param decodedTile - The decoded tile to set. */ set decodedTile(decodedTile) { var _a, _b; this.m_decodedTile = decodedTile; this.invalidateResourceInfo(); if (decodedTile === undefined) { return; } if (decodedTile.geometries.length === 0) { this.forceHasGeometry(true); } // If the decoder provides a more accurate bounding box than the one we computed from // the flat geo box we take it instead. Otherwise, if an elevation range was set, elevate // bounding box to match the elevated geometry. this.m_maxGeometryHeight = decodedTile.boundingBox ? undefined : (_a = decodedTile.maxGeometryHeight) !== null && _a !== void 0 ? _a : 0; this.m_minGeometryHeight = decodedTile.boundingBox ? undefined : (_b = decodedTile.minGeometryHeight) !== null && _b !== void 0 ? _b : 0; this.elevateGeoBox(); this.updateBoundingBox(decodedTile.boundingBox); const stats = Statistics_1.PerformanceStatistics.instance; if (stats.enabled && decodedTile.decodeTime !== undefined) { stats.currentFrame.addValue("decode.decodingTime", decodedTile.decodeTime); stats.currentFrame.addValue("decode.decodedTiles", 1); } if (decodedTile.copyrightHolderIds !== undefined) { this.copyrightInfo = decodedTile.copyrightHolderIds.map(id => ({ id })); } this.dataSource.requestUpdate(); } /** * Called when the default implementation of `dispose()` needs * to free the geometry of a `Tile` object. * * @param object - The object that references the geometry. * @returns `true` if the geometry can be disposed. */ shouldDisposeObjectGeometry(object) { return true; } /** * Called when the default implementation of `dispose()` needs * to free a `Tile` object's material. * * @param object - The object referencing the geometry. * @returns `true` if the material can be disposed. */ shouldDisposeObjectMaterial(object) { return true; } /** * Called when the default implementation of `dispose()` needs * to free a Texture that is part of a `Tile` object's material. * * @param texture - The texture about to be disposed. * @returns `true` if the texture can be disposed. */ shouldDisposeTexture(texture) { return this.m_ownedTextures.has(texture); } /** * Returns `true` if this `Tile` has been disposed. */ get disposed() { return this.m_disposed; } /** * `True` if all geometry of the `Tile` has been loaded. */ get allGeometryLoaded() { var _a, _b; return (_b = (_a = this.m_tileGeometryLoader) === null || _a === void 0 ? void 0 : _a.isFinished) !== null && _b !== void 0 ? _b : this.hasGeometry; } /** * MapView checks if this `Tile` is ready to be rendered while culling. * * By default, MapView checks if the [[objects]] list is not empty. However, you can override * this check by manually setting this property. */ get hasGeometry() { if (this.m_forceHasGeometry === undefined) { return this.objects.length !== 0; } else { return this.m_forceHasGeometry; } } /** * Overrides the default value for [[hasGeometry]] if value is not `undefined`. * * @param value - A new value for the [[hasGeometry]] flag. */ forceHasGeometry(value) { this.m_forceHasGeometry = value; } /** * Reset the visibility counter. This will force the visibility check to be rerun on all objects * in this `Tile`. */ resetVisibilityCounter() { this.visibilityCounter = -1; } /** * Gets the {@link ITileLoader} that manages this tile. */ get tileLoader() { return this.m_tileLoader; } /** * Sets the {@link ITileLoader} to manage this tile. * * @param tileLoader - A {@link ITileLoader} instance to manage * the loading process for this tile. */ set tileLoader(tileLoader) { this.m_tileLoader = tileLoader; } /** * Loads this `Tile` geometry. * * @returns Promise which can be used to wait for the loading to be finished. */ async load() { const tileLoader = this.tileLoader; if (tileLoader === undefined) { return await Promise.resolve(); } if (this.m_tileGeometryLoader) { const wasSettled = this.m_tileGeometryLoader.isSettled; this.m_tileGeometryLoader.reset(); if (wasSettled) { this.attachGeometryLoadedCallback(); } } return await tileLoader .loadAndDecode() .then(tileLoaderState => { var _a; harp_utils_1.assert(tileLoaderState === ITileLoader_1.TileLoaderState.Ready); const decodedTile = tileLoader.decodedTile; this.decodedTile = decodedTile; (_a = decodedTile === null || decodedTile === void 0 ? void 0 : decodedTile.dependencies) === null || _a === void 0 ? void 0 : _a.forEach(mortonCode => { this.dependencies.push(harp_geoutils_1.TileKey.fromMortonCode(mortonCode)); }); }) .catch(tileLoaderState => { if (tileLoaderState === ITileLoader_1.TileLoaderState.Failed) { this.dispose(); } else if (tileLoaderState !== ITileLoader_1.TileLoaderState.Canceled) { logger.error("Unknown error" + tileLoaderState); } }); } /** * Text style cache for this tile. * @hidden */ get textStyleCache() { return this.m_textStyleCache; } /** * Frees the rendering resources allocated by this `Tile`. * * @remarks * The default implementation of this method frees the geometries and the materials for all the * reachable objects. * Textures are freed if they are owned by this `Tile` (i.e. if they where created by this * `Tile`or if the ownership was explicitely set to this `Tile` by [[addOwnedTexture]]). */ clear() { const disposeMaterial = (material) => { Object.getOwnPropertyNames(material).forEach((property) => { const materialProperty = material[property]; if (materialProperty !== undefined && materialProperty instanceof THREE.Texture) { const texture = materialProperty; if (this.shouldDisposeTexture(texture)) { texture.dispose(); } } }); material.dispose(); }; const disposeObject = (object) => { if (this.shouldDisposeObjectGeometry(object)) { if (object.geometry !== undefined) { object.geometry.dispose(); } if (object.geometries !== undefined) { for (const geometry of object.geometries) { geometry.dispose(); } } } if (object.material !== undefined && this.shouldDisposeObjectMaterial(object)) { if (object.material instanceof Array) { object.material.forEach((material) => { if (material !== undefined) { disposeMaterial(material); } }); } else { disposeMaterial(object.material); } } }; this.objects.forEach((rootObject) => { rootObject.traverse((object) => { disposeObject(object); }); disposeObject(rootObject); }); this.objects.length = 0; if (this.preparedTextPaths) { this.preparedTextPaths = []; } this.m_textStyleCache.clear(); this.clearTextElements(); this.invalidateResourceInfo(); } /** * Removes all {@link TextElement} from the tile. */ clearTextElements() { if (!this.hasTextElements()) { return; } this.textElementsChanged = true; this.m_pathBlockingElements.splice(0); this.textElementGroups.forEach((element) => { element.dispose(); }); this.textElementGroups.clear(); } /** * Adds a callback that will be called whenever the tile is disposed. * * @remarks * Multiple callbacks may be added. * @internal * @param callback - The callback to be called when the tile is disposed. */ addDisposeCallback(callback) { this.m_disposeCallback = harp_utils_1.chainCallbacks(this.m_disposeCallback, callback); } /** * Disposes this `Tile`, freeing all geometries and materials for the reachable objects. */ dispose() { var _a; if (this.m_disposed) { return; } if (this.m_tileLoader) { this.m_tileLoader.cancel(); this.m_tileLoader = undefined; } this.clear(); // Ensure that tile is removable from tile cache. this.frameNumLastRequested = 0; this.m_disposed = true; (_a = this.m_tileGeometryLoader) === null || _a === void 0 ? void 0 : _a.dispose(); if (this.m_disposeCallback) { this.m_disposeCallback(this); } } /** * Computes the offset in the x world coordinates corresponding to this tile, based on * its {@link offset}. * * @returns The x offset. */ computeWorldOffsetX() { return this.projection.worldExtent(0, 0).max.x * this.offset; } /** * Update tile for current map view zoom level * @param zoomLevel - Zoom level of the map view * @internal */ update(zoomLevel) { for (const object of this.objects) { if (object instanceof LodMesh_1.LodMesh) { object.setLevelOfDetail(zoomLevel - this.tileKey.level); } } } /** * Gets the tile's bounding box. */ get boundingBox() { return this.m_boundingBox; } /** * Start with or continue with loading geometry for tiles requiring this step. Called * repeatedly until loading is finished. * @param priority - Priority assigned to asynchronous tasks doing the geometry update. * @param enabledKinds - {@link GeometryKind}s that will be created. * @param disabledKinds - {@link GeometryKind}s that will not be created. * @return `true` if tile uses a geometry loader, `false` otherwise. * @internal */ updateGeometry(priority, enabledKinds, disabledKinds) { if (!this.m_tileGeometryLoader) { return false; } if (this.m_tileGeometryLoader.isSettled) { return true; } if (this.dataSource.isDetached()) { this.m_tileGeometryLoader.cancel(); return true; } if (this.tileLoader) { if (!this.tileLoader.isFinished) { return true; } else if (!this.decodedTile) { // Finish loading if tile has no data. this.m_tileGeometryLoader.finish(); return true; } } if (priority !== undefined) { this.m_tileGeometryLoader.priority = priority; } this.m_tileGeometryLoader.update(enabledKinds, disabledKinds); return true; } /** * Gets a set of the {@link GeometryKind}s that were loaded (if any). * @internal */ get loadedGeometryKinds() { var _a; return (_a = this.m_tileGeometryLoader) === null || _a === void 0 ? void 0 : _a.availableGeometryKinds; } /** * Called when {@link TileGeometryLoader} is finished. * * @remarks * It may be used to add content to the `Tile`. * The {@link @here/harp-datasource-protocol#DecodedTile} is still available. */ loadingFinished() { // To be used in subclasses. } attachGeometryLoadedCallback() { harp_utils_1.assert(this.m_tileGeometryLoader !== undefined); this.m_tileGeometryLoader.waitFinished() .then(() => { this.loadingFinished(); this.removeDecodedTile(); }) .catch(() => { if (this.disposed) { return; } // Loader was canceled, dispose tile. if (!this.dataSource.isDetached()) { this.mapView.visibleTileSet.disposeTile(this); } }); } /** * Remove the decodedTile when no longer needed. */ removeDecodedTile() { this.m_decodedTile = undefined; this.invalidateResourceInfo(); } /** * Updates the tile's world bounding box. * @param newBoundingBox - The new bounding box to set. If undefined, the bounding box will be * computed by projecting the tile's geoBox. */ updateBoundingBox(newBoundingBox) { if (newBoundingBox) { this.m_boundingBox.copy(newBoundingBox); this.m_worldCenter.copy(this.boundingBox.position); } else { this.projection.projectBox(this.geoBox, this.boundingBox); } } /** * Elevates the tile's geo box using the elevation range and maximum geometry height. */ elevateGeoBox() { var _a, _b; this.geoBox.southWest.altitude = this.m_elevationRange.minElevation + ((_a = this.m_minGeometryHeight) !== null && _a !== void 0 ? _a : 0); this.geoBox.northEast.altitude = this.m_elevationRange.maxElevation + ((_b = this.m_maxGeometryHeight) !== null && _b !== void 0 ? _b : 0); } computeResourceInfo() { let heapSize = 0; let num3dObjects = 0; let numTextElements = 0; const aggregatedObjSize = { heapSize: 0, gpuSize: 0 }; // Keep a map of the uuids of the larger objects, like Geometries, Materials and Attributes. // They should be counted only once even if they are shared. const visitedObjects = new Map(); for (const object of this.objects) { if (object.visible) { num3dObjects++; } Object3DUtils_1.Object3DUtils.estimateSize(object, aggregatedObjSize, visitedObjects); } for (const group of this.textElementGroups.groups) { numTextElements += group[1].elements.length; } // 216 was the shallow size of a single TextElement last time it has been checked, 312 bytes // was the minimum retained size of a TextElement that was not being rendered. If a // TextElement is actually rendered, the size may be _much_ bigger. heapSize += numTextElements * 312; if (this.m_decodedTile !== undefined && this.m_decodedTile.tileInfo !== undefined) { aggregatedObjSize.heapSize += this.m_decodedTile.tileInfo.numBytes; } this.m_resourceInfo = { heapSize: aggregatedObjSize.heapSize + heapSize, gpuSize: aggregatedObjSize.gpuSize, num3dObjects, numTextElements, numUserTextElements: 0 }; } } exports.Tile = Tile; //# sourceMappingURL=Tile.js.map