@allmaps/openlayers
Version:
OpenLayers classes for Allmaps
670 lines (669 loc) • 19.7 kB
JavaScript
import Layer from "ol/layer/Layer.js";
import { WebGL2Renderer } from "@allmaps/render/webgl2";
import { Viewport, WarpedMapEventType, WarpedMapEvent } from "@allmaps/render";
import { hexToFractionalRgb } from "@allmaps/stdlib";
import { OLWarpedMapEvent } from "./OLWarpedMapEvent.js";
class WarpedMapLayer extends Layer {
container;
canvas;
gl;
canvasSize = [0, 0];
renderer;
resizeObserver;
/**
* Creates a WarpedMapLayer instance
* @param options - the WebGL2 renderer options
*/
constructor(options) {
super({});
const container = document.createElement("div");
this.container = container;
container.style.position = "absolute";
container.style.width = "100%";
container.style.height = "100%";
container.classList.add("ol-layer");
container.classList.add("allmaps-warped-map-layer");
const canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0";
canvas.style.width = "100%";
canvas.style.height = "100%";
container.appendChild(canvas);
const gl = canvas.getContext("webgl2", {
premultipliedAlpha: true
});
if (!gl) {
throw new Error("WebGL 2 not available");
}
this.resizeObserver = new ResizeObserver(this.resized.bind(this));
this.resizeObserver.observe(canvas, { box: "content-box" });
this.canvas = canvas;
this.gl = gl;
this.renderer = new WebGL2Renderer(this.gl, options);
this.addEventListeners();
}
/**
* Adds a [Georeference Annotation](https://iiif.io/api/extension/georef/).
* @param annotation - Georeference Annotation
* @returns - the map IDs of the maps that were added, or an error per map
*/
async addGeoreferenceAnnotation(annotation) {
const results = await this.renderer.warpedMapList.addGeoreferenceAnnotation(annotation);
this.changed();
return results;
}
/**
* Removes a [Georeference Annotation](https://iiif.io/api/extension/georef/).
* @param annotation - Georeference Annotation
* @returns - the map IDs of the maps that were removed, or an error per map
*/
async removeGeoreferenceAnnotation(annotation) {
const results = await this.renderer.warpedMapList.removeGeoreferenceAnnotation(annotation);
this.changed();
return results;
}
/**
* Adds a [Georeference Annotation](https://iiif.io/api/extension/georef/) by URL.
* @param annotationUrl - Georeference Annotation
* @returns - the map IDs of the maps that were added, or an error per map
*/
async addGeoreferenceAnnotationByUrl(annotationUrl) {
const annotation = await fetch(annotationUrl).then(
(response) => response.json()
);
const results = this.addGeoreferenceAnnotation(annotation);
return results;
}
/**
* Removes a [Georeference Annotation](https://iiif.io/api/extension/georef/) by URL.
* @param annotationUrl - Georeference Annotation
* @returns - the map IDs of the maps that were removed, or an error per map
*/
async removeGeoreferenceAnnotationByUrl(annotationUrl) {
const annotation = await fetch(annotationUrl).then(
(response) => response.json()
);
const results = this.removeGeoreferenceAnnotation(annotation);
return results;
}
/**
* Adds a Georeferenced map.
* @param georeferencedMap - Georeferenced map
* @returns - the map ID of the map that was added, or an error
*/
async addGeoreferencedMap(georeferencedMap) {
const result = this.renderer.warpedMapList.addGeoreferencedMap(georeferencedMap);
this.changed();
return result;
}
/**
* Removes a Georeferenced map.
* @param georeferencedMap - Georeferenced map
* @returns - the map ID of the map that was remvoed, or an error
*/
async removeGeoreferencedMap(georeferencedMap) {
const result = this.renderer.warpedMapList.removeGeoreferencedMap(georeferencedMap);
this.changed();
return result;
}
/**
* Returns the WarpedMapList object that contains a list of the warped maps of all loaded maps
* @returns the warped map list
*/
getWarpedMapList() {
return this.renderer.warpedMapList;
}
/**
* Returns a single map's warped map
* @param mapId - ID of the map
* @returns the warped map
*/
getWarpedMap(mapId) {
return this.renderer.warpedMapList.getWarpedMap(mapId);
}
/**
* Make a single map visible
* @param mapId - ID of the map
*/
showMap(mapId) {
this.renderer.warpedMapList.showMaps([mapId]);
this.changed();
}
/**
* Make multiple maps visible
* @param mapIds - IDs of the maps
*/
showMaps(mapIds) {
this.renderer.warpedMapList.showMaps(mapIds);
this.changed();
}
/**
* Make a single map invisible
* @param mapId - ID of the map
*/
hideMap(mapId) {
this.renderer.warpedMapList.hideMaps([mapId]);
this.changed();
}
/**
* Make multiple maps invisible
* @param mapIds - IDs of the maps
*/
hideMaps(mapIds) {
this.renderer.warpedMapList.hideMaps(mapIds);
this.changed();
}
/**
* Returns the visibility of a single map
* @returns - whether the map is visible
*/
isMapVisible(mapId) {
const warpedMap = this.renderer.warpedMapList.getWarpedMap(mapId);
return warpedMap?.visible;
}
/**
* Sets the resource mask of a single map
* @param mapId - ID of the map
* @param resourceMask - new resource mask
*/
setMapResourceMask(mapId, resourceMask) {
this.renderer.warpedMapList.setMapResourceMask(resourceMask, mapId);
this.changed();
}
/**
* Sets the GCOs of a single map
* @param mapId - ID of the map
* @param gcos - new GCPs
*/
setMapGcps(mapId, gcps) {
this.renderer.warpedMapList.setMapGcps(gcps, mapId);
this.changed();
}
/**
* Sets the transformation type of multiple maps
* @param mapIds - IDs of the maps
* @param transformation - new transformation type
*/
setMapsTransformationType(mapIds, transformation) {
this.renderer.warpedMapList.setMapsTransformationType(transformation, {
mapIds
});
this.changed();
}
/**
* Sets the transformation type of a single map
* @param mapId - ID of the map
* @param transformation - new transformation type
*/
setMapTransformationType(mapId, transformation) {
this.renderer.warpedMapList.setMapTransformationType(transformation, mapId);
this.changed();
}
/**
* Sets the distortion measure of multiple maps
* @param mapIds - IDs of the maps
* @param distortionMeasure - new distortion measure
*/
setMapsDistortionMeasure(mapIds, distortionMeasure) {
this.renderer.warpedMapList.setMapsDistortionMeasure(distortionMeasure, {
mapIds
});
this.changed();
}
removeGeoreferencedMapById(mapId) {
this.renderer.warpedMapList.removeGeoreferencedMapById(mapId);
this.changed();
}
/**
* Return the bounding box of all visible maps in the layer (inside or outside of the Viewport), in longitude/latitude coordinates.
* @returns - Bounding box of all warped maps
*/
getLonLatExtent() {
return this.renderer.warpedMapList.getMapsBbox({
projection: { definition: "EPSG:4326" }
});
}
/**
* Return the bounding box of all visible maps in the layer (inside or outside of the Viewport), in projected coordinates.
* @returns - bounding box of all warped maps
*/
getExtent() {
return this.renderer.warpedMapList.getMapsBbox();
}
/**
* Bring maps to front
* @param mapIds - IDs of the maps
*/
bringMapsToFront(mapIds) {
this.renderer.warpedMapList.bringMapsToFront(mapIds);
this.changed();
}
/**
* Send maps to back
* @param mapIds - IDs of the maps
*/
sendMapsToBack(mapIds) {
this.renderer.warpedMapList.sendMapsToBack(mapIds);
this.changed();
}
/**
* Bring maps forward
* @param mapIds - IDs of the maps
*/
bringMapsForward(mapIds) {
this.renderer.warpedMapList.bringMapsForward(mapIds);
this.changed();
}
/**
* Send maps backward
* @param mapIds - IDs of the maps
*/
sendMapsBackward(mapIds) {
this.renderer.warpedMapList.sendMapsBackward(mapIds);
this.changed();
}
/**
* Returns the z-index of a single map
* @param mapId - ID of the warped map
* @returns - z-index of the warped map
*/
getMapZIndex(mapId) {
return this.renderer.warpedMapList.getMapZIndex(mapId);
}
/**
* Sets the object that caches image information
* @param imageInformations - Object that caches image information
*/
setImageInformations(imageInformations) {
this.renderer.warpedMapList.setImageInformations(imageInformations);
}
/**
* Gets the HTML container element of the layer
* @returns HTML element
*/
getContainer() {
return this.container;
}
/**
* Gets the HTML canvas element of the layer
* @returns HTML Canvas element
*/
getCanvas() {
return this.canvas;
}
/**
* Sets the options
*
* @param options - Options
*/
setOptions(options) {
this.renderer.setOptions(options);
}
// No setOpacity() and getOpacity() here since default for OL Layer class
/**
* Gets the opacity of a single map
* @param mapId - ID of the map
* @returns Opacity of the map
*/
getMapOpacity(mapId) {
return this.renderer.getMapOpacity(mapId);
}
/**
* Sets the opacity of a single map
* @param mapId - ID of the map
* @param opacity - opacity between 0 and 1, where 0 is fully transparent and 1 is fully opaque
*/
setMapOpacity(mapId, opacity) {
this.renderer.setMapOpacity(mapId, opacity);
this.changed();
}
/**
* Resets the opacity of a single map to fully opaque
* @param mapId - ID of the map
*/
resetMapOpacity(mapId) {
this.renderer.resetMapOpacity(mapId);
this.changed();
}
/**
* Sets the saturation of a single map
* @param saturation - saturation between 0 and 1, where 0 is grayscale and 1 are the original colors
*/
setSaturation(saturation) {
this.renderer.setSaturation(saturation);
this.changed();
}
/**
* Resets the saturation of a single map to the original colors
*/
resetSaturation() {
this.renderer.resetSaturation();
this.changed();
}
/**
* Sets the saturation of a single map
* @param mapId - ID of the map
* @param saturation - saturation between 0 and 1, where 0 is grayscale and 1 are the original colors
*/
setMapSaturation(mapId, saturation) {
this.renderer.setMapSaturation(mapId, saturation);
this.changed();
}
/**
* Resets the saturation of a single map to the original colors
* @param mapId - ID of the map
*/
resetMapSaturation(mapId) {
this.renderer.resetMapSaturation(mapId);
this.changed();
}
/**
* Removes a color from all maps
* @param transformOptions - remove color options
* @param transformOptions.hexColor - hex color to remove
* @param transformOptions.threshold - threshold between 0 and 1
* @param transformOptions.hardness - hardness between 0 and 1
*/
setRemoveColor(options) {
const color = options.hexColor ? hexToFractionalRgb(options.hexColor) : void 0;
this.renderer.setRemoveColorOptions({
color,
threshold: options.threshold,
hardness: options.hardness
});
this.changed();
}
/**
* Resets the color removal for all maps
*/
resetRemoveColor() {
this.renderer.resetRemoveColorOptions();
this.changed();
}
/**
* Removes a color from a single map
* @param mapId - ID of the map
* @param transformOptions - remove color options
* @param transformOptions.hexColor] - hex color to remove
* @param transformOptions.threshold] - threshold between 0 and 1
* @param transformOptions.hardness] - hardness between 0 and 1
*/
setMapRemoveColor(mapId, options) {
const color = options.hexColor ? hexToFractionalRgb(options.hexColor) : void 0;
this.renderer.setMapRemoveColorOptions(mapId, {
color,
threshold: options.threshold,
hardness: options.hardness
});
this.changed();
}
/**
* Resets the color for a single map
* @param mapId - ID of the map
*/
resetMapRemoveColor(mapId) {
this.renderer.resetMapRemoveColorOptions(mapId);
}
/**
* Sets the colorization for all maps
* @param hexColor - desired hex color
*/
setColorize(hexColor) {
const color = hexToFractionalRgb(hexColor);
if (color) {
this.renderer.setColorizeOptions({ color });
this.changed();
}
}
/**
* Resets the colorization for all maps
*/
resetColorize() {
this.renderer.resetColorizeOptions();
this.changed();
}
/**
* Sets the colorization for a single mapID of the map
* @param mapId - ID of the map
* @param hexColor - desired hex color
*/
setMapColorize(mapId, hexColor) {
const color = hexToFractionalRgb(hexColor);
if (color) {
this.renderer.setMapColorizeOptions(mapId, { color });
this.changed();
}
}
/**
* Resets the colorization of a single map
* @param mapId - ID of the map
*/
resetMapColorize(mapId) {
this.renderer.resetMapColorizeOptions(mapId);
this.changed();
}
/**
* Sets the grid for all maps
* @param enabled - whether to show the grid
*/
setGrid(enabled) {
this.renderer.setGridOptions({ enabled });
this.changed();
}
/**
* Resets the grid for all maps
*/
resetGrid() {
this.renderer.resetGridOptions();
this.changed();
}
/**
* Sets the grid for a single mapID of the map
* @param mapId - ID of the map
* @param enabled - whether to show the grid
*/
setMapGrid(mapId, enabled) {
this.renderer.setMapGridOptions(mapId, { enabled });
this.changed();
}
/**
* Resets the grid of a single map
* @param mapId - ID of the map
*/
resetMapGrid(mapId) {
this.renderer.resetMapGridOptions(mapId);
this.changed();
}
/**
* Disposes all WebGL resources and cached tiles
*/
dispose() {
this.renderer.destroy();
const extension = this.gl.getExtension("WEBGL_lose_context");
if (extension) {
extension.loseContext();
}
const canvas = this.gl.canvas;
canvas.width = 1;
canvas.height = 1;
this.resizeObserver.disconnect();
this.removeEventListeners();
super.disposeInternal();
}
/**
* Clears: removes all maps
*/
clear() {
this.renderer.warpedMapList.clear();
this.changed();
}
/**
* Render the layer.
* @param frameState - OpenLayers frame state
* @returns The rendered element
*/
render(frameState) {
if (this.canvas) {
this.resizeCanvas(this.canvas, this.canvasSize);
}
this.renderer.setOpacity(Math.min(Math.max(this.getOpacity(), 0), 1));
const viewport = new Viewport(
frameState.size,
frameState.viewState.center,
frameState.viewState.resolution,
{
rotation: frameState.viewState.rotation,
devicePixelRatio: window.devicePixelRatio,
projection: { definition: frameState.viewState.projection.getCode() }
// TODO: add a way for viewport and renderer to understand other codes then the two default ones
}
);
this.renderer.render(viewport);
return this.container;
}
resized(entries) {
for (const entry of entries) {
const width = entry.contentRect.width;
const height = entry.contentRect.height;
const dpr = window.devicePixelRatio;
const displayWidth = Math.round(width * dpr);
const displayHeight = Math.round(height * dpr);
this.canvasSize = [displayWidth, displayHeight];
}
this.changed();
}
resizeCanvas(canvas, [width, height]) {
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
contextLost(event) {
event.preventDefault();
this.renderer.contextLost();
}
contextRestored(event) {
event.preventDefault();
this.renderer.contextRestored();
}
addEventListeners() {
this.canvas.addEventListener(
"webglcontextlost",
this.contextLost.bind(this)
);
this.canvas.addEventListener(
"webglcontextrestored",
this.contextRestored.bind(this)
);
this.renderer.addEventListener(
WarpedMapEventType.CHANGED,
this.changed.bind(this)
);
this.renderer.addEventListener(
WarpedMapEventType.IMAGEINFOLOADED,
this.changed.bind(this)
);
this.renderer.addEventListener(
WarpedMapEventType.WARPEDMAPENTER,
this.passWarpedMapEvent.bind(this)
);
this.renderer.addEventListener(
WarpedMapEventType.WARPEDMAPLEAVE,
this.passWarpedMapEvent.bind(this)
);
this.renderer.tileCache.addEventListener(
WarpedMapEventType.FIRSTMAPTILELOADED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.tileCache.addEventListener(
WarpedMapEventType.ALLREQUESTEDTILESLOADED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.addEventListener(
WarpedMapEventType.GEOREFERENCEANNOTATIONADDED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.addEventListener(
WarpedMapEventType.WARPEDMAPADDED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.addEventListener(
WarpedMapEventType.WARPEDMAPREMOVED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.addEventListener(
WarpedMapEventType.VISIBILITYCHANGED,
this.changed.bind(this)
);
this.renderer.warpedMapList.addEventListener(
WarpedMapEventType.CLEARED,
this.changed.bind(this)
);
}
removeEventListeners() {
this.canvas.removeEventListener(
"webglcontextlost",
this.contextLost.bind(this)
);
this.canvas.removeEventListener(
"webglcontextrestored",
this.contextRestored.bind(this)
);
this.renderer.removeEventListener(
WarpedMapEventType.CHANGED,
this.changed.bind(this)
);
this.renderer.removeEventListener(
WarpedMapEventType.IMAGEINFOLOADED,
this.changed.bind(this)
);
this.renderer.removeEventListener(
WarpedMapEventType.WARPEDMAPENTER,
this.passWarpedMapEvent.bind(this)
);
this.renderer.removeEventListener(
WarpedMapEventType.WARPEDMAPLEAVE,
this.passWarpedMapEvent.bind(this)
);
this.renderer.tileCache.removeEventListener(
WarpedMapEventType.FIRSTMAPTILELOADED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.tileCache.removeEventListener(
WarpedMapEventType.ALLREQUESTEDTILESLOADED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.removeEventListener(
WarpedMapEventType.GEOREFERENCEANNOTATIONADDED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.removeEventListener(
WarpedMapEventType.WARPEDMAPADDED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.removeEventListener(
WarpedMapEventType.WARPEDMAPREMOVED,
this.passWarpedMapEvent.bind(this)
);
this.renderer.warpedMapList.removeEventListener(
WarpedMapEventType.VISIBILITYCHANGED,
this.changed.bind(this)
);
this.renderer.warpedMapList.removeEventListener(
WarpedMapEventType.CLEARED,
this.changed.bind(this)
);
}
passWarpedMapEvent(event) {
if (event instanceof WarpedMapEvent) {
const olEvent = new OLWarpedMapEvent(event.type, event.data);
this.dispatchEvent(olEvent);
}
}
}
export {
WarpedMapLayer
};
//# sourceMappingURL=WarpedMapLayer.js.map