UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

337 lines (315 loc) 9.7 kB
import Color from "../Core/Color.js"; import defined from "../Core/defined.js"; import srgbToLinear from "../Core/srgbToLinear.js"; /** * This class implements an I3S symbology for I3S Layers. * <p> * Do not construct this directly, instead access symbology through {@link I3SLayer}. * </p> * @alias I3SSymbology * @internalConstructor */ function I3SSymbology(layer) { this._layer = layer; this._defaultSymbology = undefined; this._valueFields = []; this._uniqueValueHash = undefined; this._classBreaksHash = undefined; this._parseLayerSymbology(); } Object.defineProperties(I3SSymbology.prototype, { /** * Gets the default symbology data. * @memberof I3SSymbology.prototype * @type {object} * @readonly */ defaultSymbology: { get: function () { return this._defaultSymbology; }, }, }); function convertColor(color, transparency) { // color is represented as a three or four-element array, values range from 0 through 255. // transparency value has to lie between 100 (full transparency) and 0 (full opacity). const convertedColor = []; for (let i = 0; i < color.length; i++) { const floatColor = Color.byteToFloat(color[i]); if (i < 3) { convertedColor.push(srgbToLinear(floatColor)); } else { convertedColor.push(floatColor); } } if (convertedColor.length === 3) { if (defined(transparency)) { convertedColor.push(1.0 - transparency / 100.0); } else { convertedColor.push(1.0); } } return convertedColor; } function parseSymbol(symbol, isColorCaptured) { const symbology = { edges: undefined, material: undefined, }; if (defined(symbol) && defined(symbol.symbolLayers)) { for (let i = 0; i < symbol.symbolLayers.length; i++) { const symbolLayer = symbol.symbolLayers[i]; if (symbolLayer.type === "Fill") { const edges = symbolLayer.edges; const outline = symbolLayer.outline; if (defined(edges)) { symbology.edges = {}; if (defined(edges.color)) { symbology.edges.color = convertColor( edges.color, edges.transparency, ); } } else if (defined(outline)) { symbology.edges = {}; if (defined(outline.color)) { symbology.edges.color = convertColor( outline.color, outline.transparency, ); } } if (!isColorCaptured) { const material = symbolLayer.material; if (defined(material)) { symbology.material = { colorMixMode: material.colorMixMode, }; if (defined(material.color)) { symbology.material.color = convertColor( material.color, material.transparency, ); } } } break; } } } return symbology; } function buildUniqueValueHash(renderer, isColorCaptured) { if (defined(renderer.uniqueValueGroups)) { const valueHash = {}; for ( let groupIndex = 0; groupIndex < renderer.uniqueValueGroups.length; groupIndex++ ) { const classes = renderer.uniqueValueGroups[groupIndex].classes; if (defined(classes)) { for (let classIndex = 0; classIndex < classes.length; classIndex++) { const classSymbology = parseSymbol( classes[classIndex].symbol, isColorCaptured, ); const values = classes[classIndex].values; for (let valueIndex = 0; valueIndex < values.length; valueIndex++) { const fieldValues = values[valueIndex]; let hash = valueHash; for ( let fieldIndex = 0; fieldIndex < fieldValues.length; fieldIndex++ ) { const fieldValue = fieldValues[fieldIndex]; if (fieldIndex === fieldValues.length - 1) { hash[fieldValue] = classSymbology; } else { if (!defined(hash[fieldValue])) { hash[fieldValue] = {}; } hash = hash[fieldValue]; } } } } } } return valueHash; } if (defined(renderer.uniqueValueInfos)) { const valueHash = {}; for ( let infoIndex = 0; infoIndex < renderer.uniqueValueInfos.length; infoIndex++ ) { const info = renderer.uniqueValueInfos[infoIndex]; valueHash[info.value] = parseSymbol(info.symbol, isColorCaptured); } return valueHash; } return undefined; } function buildClassBreaksHash(renderer, isColorCaptured) { if (defined(renderer.classBreakInfos)) { const classBreakInfos = [...renderer.classBreakInfos]; classBreakInfos.sort(function (a, b) { const aMax = a.classMaxValue ?? a.classMinValue; const bMax = b.classMaxValue ?? b.classMinValue; return aMax - bMax; }); const valueHash = { ranges: [], symbols: [], }; if (defined(renderer.minValue)) { valueHash.ranges.push(renderer.minValue); valueHash.symbols.push(undefined); } for (let infoIndex = 0; infoIndex < classBreakInfos.length; infoIndex++) { const info = classBreakInfos[infoIndex]; if (defined(info.classMinValue)) { if ( valueHash.ranges.length === 0 || info.classMinValue > valueHash.ranges[valueHash.ranges.length - 1] ) { valueHash.ranges.push(info.classMinValue); valueHash.symbols.push(undefined); } } if (defined(info.classMaxValue)) { if ( valueHash.ranges.length === 0 || info.classMaxValue > valueHash.ranges[valueHash.ranges.length - 1] ) { valueHash.ranges.push(info.classMaxValue); valueHash.symbols.push(parseSymbol(info.symbol, isColorCaptured)); } } } valueHash.symbols.push(undefined); return valueHash; } return undefined; } /** * @private */ I3SSymbology.prototype._parseLayerSymbology = function () { const drawingInfo = this._layer.data.drawingInfo; if (defined(drawingInfo) && defined(drawingInfo.renderer)) { const cachedDrawingInfo = this._layer.data.cachedDrawingInfo; const isColorCaptured = defined(cachedDrawingInfo) && cachedDrawingInfo.color === true; const renderer = drawingInfo.renderer; if (renderer.type === "simple") { this._defaultSymbology = parseSymbol(renderer.symbol, isColorCaptured); } else if (renderer.type === "uniqueValue") { this._defaultSymbology = parseSymbol( renderer.defaultSymbol, isColorCaptured, ); this._valueFields.push(renderer.field1); if (defined(renderer.field2)) { this._valueFields.push(renderer.field2); } if (defined(renderer.field3)) { this._valueFields.push(renderer.field3); } this._uniqueValueHash = buildUniqueValueHash(renderer, isColorCaptured); } else if (renderer.type === "classBreaks") { this._defaultSymbology = parseSymbol( renderer.defaultSymbol, isColorCaptured, ); this._valueFields.push(renderer.field); this._classBreaksHash = buildClassBreaksHash(renderer, isColorCaptured); } } }; function findHashForUniqueValues(hash, values, hashLevel, valueIndex) { const levelValues = values[hashLevel]; if (valueIndex < levelValues.length) { const hashValue = levelValues[valueIndex]; const innerHash = hash[hashValue]; if (defined(innerHash) && ++hashLevel < values.length) { return findHashForUniqueValues(innerHash, values, hashLevel, valueIndex); } return innerHash; } return undefined; } function bisect(array, value) { let low = 0; let high = array.length; if (low < high) { do { const mid = (low + high) >>> 1; if (array[mid] < value) { low = mid + 1; } else { high = mid; } } while (low < high); } return low; } function findHashForClassBreaks(hash, values, valueIndex) { const value = values[valueIndex]; const range = bisect(hash.ranges, value); return hash.symbols[range]; } /** * @private */ I3SSymbology.prototype._getSymbology = async function (node) { const symbology = { default: this._defaultSymbology, }; if (this._valueFields.length > 0) { const promises = []; for (let i = 0; i < this._valueFields.length; i++) { promises.push(node.loadField(this._valueFields[i])); } await Promise.all(promises); const fieldsValues = []; for (let i = 0; i < this._valueFields.length; i++) { fieldsValues.push(node.fields[this._valueFields[i]].values); } let featureHashFn; if (defined(this._uniqueValueHash)) { featureHashFn = (featureIndex) => findHashForUniqueValues( this._uniqueValueHash, fieldsValues, 0, featureIndex, ); } else if (defined(this._classBreaksHash)) { featureHashFn = (featureIndex) => findHashForClassBreaks( this._classBreaksHash, fieldsValues[0], featureIndex, ); } if (defined(featureHashFn)) { const firstFieldValues = fieldsValues[0]; for ( let featureIndex = 0; featureIndex < firstFieldValues.length; featureIndex++ ) { const featureSymbology = featureHashFn(featureIndex); if (defined(featureSymbology)) { symbology[featureIndex] = featureSymbology; } } } } return symbology; }; export default I3SSymbology;