@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
JavaScript
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;