import type Point from "../../../geometry/Point.js"; import type Layer from "../../../layers/Layer.js"; import type MapView from "../../MapView.js"; import type ViewState from "../ViewState.js"; import type LayerView from "../../layers/LayerView.js"; import type { ScreenPoint } from "../../../core/types.js"; import type { ViewHit } from "../../types.js"; import type { LayerViewProperties } from "../../layers/LayerView.js"; export interface BaseLayerView2DProperties extends LayerViewProperties, Partial> {} export interface RenderParameters { /** The [canvas 2D context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) in which to draw content. */ context: CanvasRenderingContext2D; /** The pixel ratio. */ pixelRatio: number; /** The object that describes view state. */ state: ViewState; /** The stationary state of the `MapView`. */ stationary: boolean; } /** Represents a tile reference. */ export interface Tile { /** The tile string identifier in the format `level/row/col/world` */ id: string; /** The level identifier of the [LOD](https://developers.arcgis.com/javascript/latest/references/core/layers/support/LOD/) to which the tile belongs */ level: number; /** The row identifier. */ row: number; /** The column identifier. */ col: number; /** When the projection allows world wrapping (e.g. Web Mercator), identifies the instance of the world this tile's `level`/`row`/`col`. */ world: number; /** The number of map units per pixel in the tile. */ resolution: number; /** The map scale at the tile's level. */ scale: number; /** The coordinates of the top-left corner of the tile as an array of two numbers. The coordinates are in un-normalized map units. */ coords: [ number, number ]; /** The bounds of the tile as an array of four numbers that be readily converted to an [Extent](https://developers.arcgis.com/javascript/latest/references/core/geometry/Extent/) object. */ bounds: [ number, number, number, number ]; } /** * Represents the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) of a [Layer](https://developers.arcgis.com/javascript/latest/references/core/layers/Layer/) * after it has been added to a [Map](https://developers.arcgis.com/javascript/latest/references/core/Map/) with a [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/). * * This class may be extended to create a custom [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) for a Layer. * A [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is created on demand by the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) when a layer is * added the to list of layers of its map. * * The subclass can implement multiple functions of the LayerView lifecycle. First, the [attach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#attach) method * is called when the LayerView is about to start drawing the layer's content. Then during the life of the LayerView, * the [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#render) method is called during the MapView rendering phase. The `render()` method has access to a canvas 2d context * in which it can render the content available for display. Finally the [detach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#detach) method is called after the layer * is removed from the map. It releases all allocated resources and stops on-going requests. * * @since 4.8 * @example * let TileBorderLayerView2D = BaseLayerView2D.createSubclass({ * // Example of a render implementation that draws tile boundaries * render(renderParameters) { * let tileSize = this.layer.tileInfo.size[0]; * let state = renderParameters.state; * let pixelRatio = state.pixelRatio; * let width = state.size[0]; * let height = state.size[1]; * let context = renderParameters.context; * let coords = [0, 0]; * * context.fillStyle = "rgba(0,0,0,0.25)"; * context.fillRect(0, 0, width * pixelRatio, height * pixelRatio); * * // apply rotation for everything that will be applied to the canvas * if (state.rotation !== 0) { * context.translate(width * pixelRatio * 0.5, height * pixelRatio * 0.5); * context.rotate((state.rotation * Math.PI) / 180); * context.translate(- width * pixelRatio * 0.5, -height * pixelRatio * 0.5); * } * * // Set the style for all the text. * context.font = "24px monospace"; * context.fillStyle = "black"; * context.shadowBlur = 1; * * for (const tile of this.tiles) { * let screenScale = tile.resolution / state.resolution * pixelRatio; * * state.toScreenNoRotation(coords, tile.coords); * * // Draw the tile boundaries * context.strokeRect(coords[0], coords[1], tileSize * screenScale, tileSize * screenScale); * * // Draw the tile information * context.shadowColor = "white"; * context.fillText( * tile.level + "/" + tile.row + "/" + tile.col + "/" + tile.world, * coords[0] + 12, * coords[1] + 24, * tileSize * screenScale * ); * context.shadowColor = "transparent"; * } * } * }); * * let CustomTileLayer = Layer.createSubclass({ * tileInfo: TileInfo.create({ spatialReference: { wkid: 3857 }}), * * createLayerView(view) { * if (view.type === "2d") { * return new TileBorderLayerView2D({ * view: view, * layer: this * }); * } * } * }); */ export default abstract class BaseLayerView2D extends LayerView { constructor(properties?: BaseLayerView2DProperties); /** References the layer this [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) represents. */ get layer(): Layer; /** * The array of [Tile](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#Tile) objects computed to cover the MapView's visible area. * This array is updated when the view is animating or the user is interacting with it. Then if tiles have been added or removed, * [tilesChanged()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#tilesChanged) is called. * * @see [tilesChanged()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#tilesChanged) */ tiles: Tile[]; /** * References the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) this [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) belongs to. * * @since 4.28 * @example * // Check for the first time layerView.updating becomes false. Then query for * // features that are visible within the view associated with the layer view. * await reactiveUtils.whenOnce(() => !layerView.updating); * const query = layerView.createQuery(); * query.geometry = layerView.view.extent; * const result = layerView.queryFeatures(query); */ get view(): MapView; /** * Method called when after the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is created and right before it's asked to draw the layer's content. * Typically this method is implemented to start watching property changes on the layer for example. * * @see [detach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#detach) * @example * attach() { * this._propertyHandle = reactiveUtils.watch( * () => this.layer.opacity, * () => this.requestRender() * ); * } */ attach(): void; /** * Method called after the layer is removed and the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is about to be removed. * Typically, this method is implemented to free resources like watchers. * * @see [attach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#attach) * @example * // remove the watchers on the layer that are added in attach() * detach() { * this._propertyHandle.remove(); * this._propertyHandle = null; * } */ detach(): void; /** * Method to implement that is responsible for providing objects hit at the specified screen coordinates. * This method is called internally by the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) each time * its [MapView.hitTest()](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/#hitTest) method is called. * * @param mapPoint - The point in map units. * @param screenPoint - The point in screen coordinates. * @returns A Promise that resolves to an array of hits. */ hitTest(mapPoint: Point, screenPoint: ScreenPoint): Promise; /** * The method to implement that is responsible of drawing the content of the layer. * This method is called every time the MapView's state changes, or if [requestRender()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/#requestRender) has been called. * * @param renderParameters * @example * // Example of a render implementation that draws tile boundaries * render(renderParameters) { * let tileSize = this.layer.tileInfo.size[0]; * let state = renderParameters.state; * let pixelRatio = state.pixelRatio; * let width = state.size[0]; * let height = state.size[1]; * let context = renderParameters.context; * let coords = [0, 0]; * * context.fillStyle = "rgba(0,0,0,0.25)"; * context.fillRect(0, 0, width * pixelRatio, height * pixelRatio); * * // apply rotation for everything that will be applied to the canvas * if (state.rotation !== 0) { * context.translate(width * pixelRatio * 0.5, height * pixelRatio * 0.5); * context.rotate((state.rotation * Math.PI) / 180); * context.translate(- width * pixelRatio * 0.5, -height * pixelRatio * 0.5); * } * * // Set the style for all the text. * context.font = "24px monospace"; * context.fillStyle = "black"; * context.shadowBlur = 1; * * for (const tile of this.tiles) { * let screenScale = tile.resolution / state.resolution * pixelRatio; * * state.toScreenNoRotation(coords, tile.coords); * * // Draw the tile boundaries * context.strokeRect(coords[0], coords[1], tileSize * screenScale, tileSize * screenScale); * * // Draw the tile information * context.shadowColor = "white"; * context.fillText( * tile.level + "/" + tile.row + "/" + tile.col + "/" + tile.world, * coords[0] + 12, * coords[1] + 24, * tileSize * screenScale * ); * context.shadowColor = "transparent"; * } * } */ abstract render(renderParameters: RenderParameters): void; /** * The [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) can call this method to ask the MapView to schedule a new rendering frame. * * @example * // Call requestRender whenever the layer opacity has changed. * attach() { * this._propertyHandle = reactiveUtils.watch( * () => this.layer.opacity, * () => this.requestRender() * ); * } */ requestRender(): void; /** * Method to implement, which notifies of tiles being added or removed for the current view state. * This function can be implemented to start and stop fetching new data, or allocate and dispose resources. * * @param added - The tile objects added for the current view viewport. * @param removed - The tile objects removed from the view viewport. */ tilesChanged(added: Tile[], removed: Tile[]): void; }