UNPKG

cesium-geoserverterrainprovider

Version:
391 lines (364 loc) 14.8 kB
import type { CustomHeightmapTerrainProvider, Ellipsoid, GeographicTilingScheme, HeightmapTerrainData, Rectangle, WebMercatorTilingScheme } from 'cesium'; const C: typeof import('cesium') = (window as any).Cesium export type Nullable<T> = T | null; export function intersectionRectangle(rectangle0: Rectangle, rectangle1: Rectangle): Nullable<Rectangle> { const west = Math.max(rectangle0.west, rectangle1.west); const east = Math.min(rectangle0.east, rectangle1.east); const south = Math.max(rectangle0.south, rectangle1.south); const north = Math.min(rectangle0.north, rectangle1.north); let resultat; if ((east <= west) || (south >= north)) { resultat = null; } else { resultat = new C.Rectangle(west, south, east, north); } return resultat; }; export interface ICRS { name: string; ellipsoid: Ellipsoid; firstAxeIsLatitude: boolean; supportedCRS: string; tilingScheme: typeof GeographicTilingScheme | typeof WebMercatorTilingScheme; } /** * static array where CRS availables for OGCHelper are defined */ export const CRS: ICRS[] = [{ name: "CRS:84", ellipsoid: C.Ellipsoid.WGS84, firstAxeIsLatitude: false, tilingScheme: C.GeographicTilingScheme, supportedCRS: "urn:ogc:def:crs:OGC:2:84" }, { name: "EPSG:4326", ellipsoid: C.Ellipsoid.WGS84, firstAxeIsLatitude: true, tilingScheme: C.GeographicTilingScheme, supportedCRS: "urn:ogc:def:crs:EPSG::4326" }, { name: "EPSG:3857", ellipsoid: C.Ellipsoid.WGS84, firstAxeIsLatitude: false, tilingScheme: C.WebMercatorTilingScheme, supportedCRS: "urn:ogc:def:crs:EPSG::3857" }, { name: "OSGEO:41001", ellipsoid: C.Ellipsoid.WGS84, firstAxeIsLatitude: false, tilingScheme: C.WebMercatorTilingScheme, supportedCRS: "urn:ogc:def:crs:EPSG::3857" }]; export function addCRS(val: ICRS) { CRS.push(val); } export interface IFormatImage { format: string; extension: string; terrainDataStructure?: ITerrainDataStructure; } /** * static array where image formats available for OGCHelper are * defined */ export const FormatImage: IFormatImage[] = [{ format: "image/png", extension: "png" }, { format: "image/jpeg", extension: "jpg" }, { format: "image/jpeg", extension: "jpeg" }, { format: "image/gif", extension: "gif" }, { format: "image/png; mode=8bit", extension: "png" }]; export function addFormatImage(val: IFormatImage) { FormatImage.push(val); } export interface ITerrainDataStructure { heightScale: number; heightOffset: number; elementsPerHeight: number; stride: number; elementMultiplier: number; isBigEndian: boolean; lowestEncodedHeight: number; highestEncodedHeight: number; } export interface IFormatArray { format: string; postProcessArray: (bufferIn: ArrayBuffer, size: ISize, highest: number, lowest: number, offset: number) => Int16Array | Float32Array; terrainDataStructure?: ITerrainDataStructure; } export interface ISize { height: number; width: number } /** * static array where data array availables for OGCHelper are defined */ export const FormatArray: IFormatArray[] = [{ format: "image/bil", /** * bufferIn : buffer to process (switch byte order and check the data limitations) * size: defines the dimension of the array (size.height* size.width cells) * highest: defines the highest altitude (without offset) of the data. * lowest: defines the lowest altitude (without offset) of the data. * offset: defines the offset of the data in order adjust the limitations */ postProcessArray: function (bufferIn, size, highest, lowest, offset) { let resultat = null; const viewerIn = new DataView(bufferIn); const littleEndianBuffer = new ArrayBuffer(size.height * size.width * 2); const viewerOut = new DataView(littleEndianBuffer); if (littleEndianBuffer.byteLength === bufferIn.byteLength) { // time to switch bytes!! let temp: number, goodCell = 0, somme = 0; for (let i = 0; i < littleEndianBuffer.byteLength; i += 2) { temp = viewerIn.getInt16(i, false) - offset; if (temp > lowest && temp < highest) { viewerOut.setInt16(i, temp, true); somme += temp; goodCell++; } else { const val = (goodCell === 0 ? 1 : somme / goodCell); viewerOut.setInt16(i, val, true); } } resultat = new Int16Array(littleEndianBuffer); } return resultat; } }]; export function addFormatArray(val: IFormatArray) { FormatArray.push(val); } export type TService = 'WMS' | 'TMS' | 'WMTS'; /** * parse wms,TMS or WMTS url from an url and a layer. request metadata information on server. * * * @param {String} * description.layerName the name of the layer. * @param {String} * [description.url] The URL of the server providing wms. * @param {String} * [description.xml] the xml after requesting "getCapabilities" * from web map server. * @param {String} * [description.service] the type of service requested (WMS,TMS,WMTS). WMS is default * from web map server. * @param {Object} * [description.proxy] A proxy to use for requests. This object * is expected to have a getURL function which returns the * proxied URL, if needed. * @param {Number} * [description.heightMapWidth] width of a tile in pixels * @param {Number} * [description.heightMapHeight] height of a tile in pixels * @param {Number} * [description.offset] offset of the tiles (in meters) * @param {Number} * [description.highest] highest altitude in the tiles (in meters) * @param {Number} * [description.lowest] lowest altitude in the tiles (in meters) * @param {String} * [description.styleName] name of the Style used for images. * @param {boolean} * [description.hasStyledImage] indicates if the requested images are styled with SLD * @param {Number} * [description.maxLevel] maximum level to request * @param {Object} * [description.formatImage] see OGCHelper.FormatImage * @param {Object} * [description.formatArray] see OGCHelper.FormatArray * return a promise with: * - ready : boolean which indicates that the parsing didn't have issue * - [URLtemplateImage]: function which takes in parameters x,y,level and return the good URL template to request an image * - [URLtemplateArray]: function which takes in parameters x,y,level and return the good URL template to request an typedArray * - highest: integer indicates the highest elevation of the terrain provider * - lowest: integer indicates the lowest elevation of the terrain provider * - offset: integer indicates the offset of the terrain * - hasStyledImage: boolean indicates if the images use a style (change the offset) * - heightMapWidth: integer with of the hightMapTerrain * - heightMapHeight: integer height of the hightMapTerrain * - getTileDataAvailable: function determines whether data for a tile is available to be loaded * - tilingScheme: the tiling scheme to use * - [imageSize]: {width:integer, height:integer} dimension of the requested images */ export interface IDescription { layerName: string; url?: string; xml?: XMLDocument; service?: TService; proxy?: { getURL: (val: string) => string }; heightMapWidth?: number; heightMapHeight?: number; offset?: number; highest?: number; lowest?: number; styleName?: string; maxLevel?: number; formatImage?: IFormatImage; formatArray?: IFormatArray; hasStyledImage?: boolean; } export const defaultDescription: Partial<IDescription> = { service: 'WMS', maxLevel: 11, heightMapWidth: 65, heightMapHeight: 65, offset: 0, highest: 12000, lowest: -500, hasStyledImage: false, }; export interface IResult { levelZeroMaximumGeometricError: number; tilingScheme: GeographicTilingScheme | WebMercatorTilingScheme; URLtemplateImage?: (x: number, y: number, level: number) => string; getHeightmapTerrainDataImage?: (x: number, y: number, level: number) => Promise<HeightmapTerrainData>; getBufferImage?: CustomHeightmapTerrainProvider.GeometryCallback; offset?: number; highest?: number; lowest?: number; heightMapWidth?: number; heightMapHeight?: number; hasStyledImage?: boolean; formatImage?: IFormatImage; formatArray?: IFormatArray | string; URLtemplateArray?: (x: number, y: number, level: number) => string; getHeightmapTerrainDataArray?: (x: number, y: number, level: number) => Promise<HeightmapTerrainData>; getBufferArray?: CustomHeightmapTerrainProvider.GeometryCallback; ready: boolean; maximumLevel: number; GeometryCallback: CustomHeightmapTerrainProvider.GeometryCallback; getTileDataAvailable: (x: number, y: number, level: number) => boolean; imageSize: { width: number, height: number }; } export const defaultArrayTerrainDataStructure: ITerrainDataStructure = { heightScale: 1.0, heightOffset: 0, elementsPerHeight: 1, stride: 1, elementMultiplier: 256.0, isBigEndian: false, lowestEncodedHeight: 0, highestEncodedHeight: 10000 }; export function basicAssignResult(description: IDescription, resultat: Partial<IResult>) { resultat.heightMapWidth = description.heightMapWidth; resultat.heightMapHeight = description.heightMapHeight; resultat.ready = false; resultat.maximumLevel = description.maxLevel; resultat.levelZeroMaximumGeometricError = undefined; resultat.offset = description.offset; resultat.highest = description.highest; resultat.lowest = description.lowest; resultat.hasStyledImage = description.hasStyledImage || typeof (description.styleName) === "string"; } export interface ILimitation { highest: number; lowest: number; offset: number; } /** * * arrayBuffer: the arrayBuffer to process to have a HeightmapTerrainData * limitations: object which defines highest (limitations.highest), lowest (limitations.lowest) altitudes * and the offset (limitations.offset) of the terrain. * size: number defining the height and width of the tile (can be a int or an object with two attributs: height and width) * formatArray: object which defines the terrainDataStructure (formatArray.terrainDataStructure) and * the postProcessArray (formatArray.postProcessArray) * childrenMask: Number defining the childrenMask * */ export function arrayToBuffer(arrayBuffer: ArrayBuffer, limitations: ILimitation, size: { width: number, height: number }, formatArray: IFormatArray) { return formatArray.postProcessArray(arrayBuffer, size, limitations.highest, limitations.lowest, limitations.offset); } /** * * image: the image to process to have a HeightmapTerrainData * limitations: object which defines highest (limitations.highest), lowest (limitations.lowest) altitudes * and the offset (limitations.offset) of the terrain. The style defined in mySLD use an offset of 32768 meters * size: number defining the height and width of the tile * childrenMask: Number defining the childrenMask */ export function imageToBuffer(image: HTMLImageElement, limitations: ILimitation, size: { width: number, height: number }, hasStyledImage) { const dataPixels: Uint8ClampedArray = C.getImagePixels(image, size.width, size.height) as any; const buffer = new Int16Array(dataPixels.length / 4); let goodCell = 0, somme = 0; for (let i = 0; i < dataPixels.length; i += 4) { const msb = dataPixels[i]; const lsb = dataPixels[i + 1]; const isCorrect = dataPixels[i + 2] > 128; const valeur = (msb << 8 | lsb) - limitations.offset - 32768; if (valeur > limitations.lowest && valeur < limitations.highest && (isCorrect || hasStyledImage)) { buffer[i / 4] = valeur; somme += valeur; goodCell++; } else { buffer[i / 4] = (goodCell === 0 ? 0 : somme / goodCell); //buffer[i / 4] = 0; } } return buffer; } /** * * arrayBuffer: the arrayBuffer to process to have a HeightmapTerrainData * limitations: object which defines highest (limitations.highest), lowest (limitations.lowest) altitudes * and the offset (limitations.offset) of the terrain. * size: number defining the height and width of the tile (can be a int or an object with two attributs: height and width) * formatArray: object which defines the terrainDataStructure (formatArray.terrainDataStructure) and * the postProcessArray (formatArray.postProcessArray) * childrenMask: Number defining the childrenMask * */ export function arrayToHeightmapTerrainData(arrayBuffer: ArrayBuffer, limitations: ILimitation, size: { width: number, height: number }, formatArray: IFormatArray, childrenMask: number) { const heightBuffer = arrayToBuffer(arrayBuffer, limitations, size, formatArray) if (heightBuffer === null) { throw new Error("no good size"); } const optionsHeihtmapTerrainData = { buffer: heightBuffer, width: size.width, height: size.height, childTileMask: childrenMask, structure: formatArray.terrainDataStructure, }; return new C.HeightmapTerrainData(optionsHeihtmapTerrainData); }; /** * * image: the image to process to have a HeightmapTerrainData * limitations: object which defines highest (limitations.highest), lowest (limitations.lowest) altitudes * and the offset (limitations.offset) of the terrain. The style defined in mySLD use an offset of 32768 meters * size: number defining the height and width of the tile * childrenMask: Number defining the childrenMask */ export function imageToHeightmapTerrainData(image: HTMLImageElement, limitations: ILimitation, size: { width: number, height: number }, childrenMask: number, hasStyledImage) { const buffer = imageToBuffer(image, limitations, size, hasStyledImage); const optionsHeihtmapTerrainData = { buffer: buffer, width: size.width, height: size.height, childTileMask: childrenMask, structure: { heightScale: 1.0, heightOffset: 0.0, elementsPerHeight: 1, stride: 1, elementMultiplier: 256.0, isBigEndian: false }, }; return new C.HeightmapTerrainData(optionsHeihtmapTerrainData); };