UNPKG

@loaders.gl/i3s

Version:
1,372 lines (1,349 loc) 56.6 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/index.js var dist_exports = {}; __export(dist_exports, { ArcGISWebSceneLoader: () => ArcGISWebSceneLoader, COORDINATE_SYSTEM: () => COORDINATE_SYSTEM, I3SAttributeLoader: () => I3SAttributeLoader, I3SBuildingSceneLayerLoader: () => I3SBuildingSceneLayerLoader, I3SContentLoader: () => I3SContentLoader, I3SLoader: () => I3SLoader, I3SNodePageLoader: () => I3SNodePageLoader, LayerError: () => LayerError, SLPKArchive: () => SLPKArchive, SLPKLoader: () => SLPKLoader, customizeColors: () => customizeColors, loadFeatureAttributes: () => loadFeatureAttributes, parseSLPKArchive: () => parseSLPKArchive }); module.exports = __toCommonJS(dist_exports); // dist/lib/parsers/constants.js var import_math = require("@loaders.gl/math"); function getConstructorForDataFormat(dataType) { switch (dataType) { case "UInt8": return Uint8Array; case "UInt16": return Uint16Array; case "UInt32": return Uint32Array; case "Float32": return Float32Array; case "UInt64": return Float64Array; default: throw new Error(`parse i3s tile content: unknown type of data: ${dataType}`); } } var GL_TYPE_MAP = { UInt8: import_math.GL.UNSIGNED_BYTE, UInt16: import_math.GL.UNSIGNED_SHORT, Float32: import_math.GL.FLOAT, UInt32: import_math.GL.UNSIGNED_INT, UInt64: import_math.GL.DOUBLE }; function sizeOf(dataType) { switch (dataType) { case "UInt8": return 1; case "UInt16": case "Int16": return 2; case "UInt32": case "Int32": case "Float32": return 4; case "UInt64": case "Int64": case "Float64": return 8; default: throw new Error(`parse i3s tile content: unknown size of data: ${dataType}`); } } var STRING_ATTRIBUTE_TYPE = "String"; var OBJECT_ID_ATTRIBUTE_TYPE = "Oid32"; var FLOAT_64_TYPE = "Float64"; var INT_16_ATTRIBUTE_TYPE = "Int16"; var COORDINATE_SYSTEM; (function(COORDINATE_SYSTEM2) { COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["DEFAULT"] = -1] = "DEFAULT"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["LNGLAT"] = 1] = "LNGLAT"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["METER_OFFSETS"] = 2] = "METER_OFFSETS"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["LNGLAT_OFFSETS"] = 3] = "LNGLAT_OFFSETS"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["CARTESIAN"] = 0] = "CARTESIAN"; })(COORDINATE_SYSTEM || (COORDINATE_SYSTEM = {})); // dist/i3s-loader.js var import_core5 = require("@loaders.gl/core"); // dist/lib/parsers/parse-i3s-tile-content.js var import_core = require("@loaders.gl/core"); var import_core2 = require("@math.gl/core"); var import_geospatial = require("@math.gl/geospatial"); var import_loader_utils = require("@loaders.gl/loader-utils"); var import_images = require("@loaders.gl/images"); var import_draco = require("@loaders.gl/draco"); var import_textures = require("@loaders.gl/textures"); // dist/types.js var HeaderAttributeProperty; (function(HeaderAttributeProperty2) { HeaderAttributeProperty2["vertexCount"] = "vertexCount"; HeaderAttributeProperty2["featureCount"] = "featureCount"; })(HeaderAttributeProperty || (HeaderAttributeProperty = {})); // dist/lib/utils/url-utils.js function getUrlWithoutParams(url) { let urlWithoutParams; try { const urlObj = new URL(url); urlWithoutParams = `${urlObj.origin}${urlObj.pathname}`; if (urlWithoutParams.startsWith("null")) { urlWithoutParams = null; } } catch (e) { urlWithoutParams = null; } return urlWithoutParams || url; } function getUrlWithToken(url, token = null) { return token ? `${url}?token=${token}` : url; } function generateTileAttributeUrls(url, tile) { const { attributeData = [] } = tile; const attributeUrls = []; for (let index = 0; index < attributeData.length; index++) { const attributeUrl = attributeData[index].href.replace("./", ""); attributeUrls.push(`${url}/${attributeUrl}`); } return attributeUrls; } function generateTilesetAttributeUrls(tileset, url, resource) { const attributeUrls = []; const { attributeStorageInfo = [] } = tileset; for (let index = 0; index < attributeStorageInfo.length; index++) { const fileName = attributeStorageInfo[index].key; attributeUrls.push(`${url}/nodes/${resource}/attributes/${fileName}/0`); } return attributeUrls; } // dist/lib/parsers/parse-i3s-tile-content.js var scratchVector = new import_core2.Vector3([0, 0, 0]); function getLoaderForTextureFormat(textureFormat) { switch (textureFormat) { case "ktx-etc2": case "dds": return import_textures.CompressedTextureLoader; case "ktx2": return import_textures.BasisLoader; case "jpg": case "png": default: return import_images.ImageLoader; } } var I3S_ATTRIBUTE_TYPE = "i3s-attribute-type"; async function parseI3STileContent(arrayBuffer, tileOptions, tilesetOptions, options, context) { var _a; const content = { attributes: {}, indices: null, featureIds: [], vertexCount: 0, modelMatrix: new import_core2.Matrix4(), coordinateSystem: 0, byteLength: 0, texture: null }; if (tileOptions.textureUrl) { const url = getUrlWithToken(tileOptions.textureUrl, (_a = options == null ? void 0 : options.i3s) == null ? void 0 : _a.token); const loader = getLoaderForTextureFormat(tileOptions.textureFormat); const fetchFunc = (context == null ? void 0 : context.fetch) || fetch; const response = await fetchFunc(url); const arrayBuffer2 = await response.arrayBuffer(); if (options == null ? void 0 : options.i3s.decodeTextures) { if (loader === import_images.ImageLoader) { const options2 = { ...tileOptions.textureLoaderOptions, image: { type: "data" } }; try { const texture = await (0, import_loader_utils.parseFromContext)(arrayBuffer2, [], options2, context); content.texture = texture; } catch (e) { const texture = await (0, import_core.parse)(arrayBuffer2, loader, options2, context); content.texture = texture; } } else if (loader === import_textures.CompressedTextureLoader || loader === import_textures.BasisLoader) { let texture = await (0, import_core.load)(arrayBuffer2, loader, tileOptions.textureLoaderOptions); if (loader === import_textures.BasisLoader) { texture = texture[0]; } content.texture = { compressed: true, mipmaps: false, width: texture[0].width, height: texture[0].height, data: texture }; } } else { content.texture = arrayBuffer2; } } content.material = makePbrMaterial(tileOptions.materialDefinition, content.texture); if (content.material) { content.texture = null; } return await parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options); } async function parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options) { var _a, _b; const contentByteLength = arrayBuffer.byteLength; let attributes; let vertexCount; let byteOffset = 0; let featureCount = 0; let indices; if (tileOptions.isDracoGeometry) { const decompressedGeometry = await (0, import_core.parse)(arrayBuffer, import_draco.DracoLoader, { draco: { attributeNameEntry: I3S_ATTRIBUTE_TYPE } }); vertexCount = decompressedGeometry.header.vertexCount; indices = (_a = decompressedGeometry.indices) == null ? void 0 : _a.value; const { POSITION, NORMAL, COLOR_0, TEXCOORD_0, ["feature-index"]: featureIndex, ["uv-region"]: uvRegion } = decompressedGeometry.attributes; attributes = { position: POSITION, normal: NORMAL, color: COLOR_0, uv0: TEXCOORD_0, uvRegion, id: featureIndex }; updateAttributesMetadata(attributes, decompressedGeometry); const featureIds = getFeatureIdsFromFeatureIndexMetadata(featureIndex); if (featureIds) { flattenFeatureIdsByFeatureIndices(attributes, featureIds); } } else { const { vertexAttributes, ordering: attributesOrder, featureAttributes, featureAttributeOrder } = tilesetOptions.store.defaultGeometrySchema; const headers = parseHeaders(arrayBuffer, tilesetOptions); byteOffset = headers.byteOffset; vertexCount = headers.vertexCount; featureCount = headers.featureCount; const { attributes: normalizedVertexAttributes, byteOffset: offset } = normalizeAttributes(arrayBuffer, byteOffset, vertexAttributes, vertexCount, attributesOrder); const { attributes: normalizedFeatureAttributes } = normalizeAttributes(arrayBuffer, offset, featureAttributes, featureCount, featureAttributeOrder); flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes); attributes = concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes); } if (!((_b = options == null ? void 0 : options.i3s) == null ? void 0 : _b.coordinateSystem) || // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison options.i3s.coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS) { const enuMatrix = parsePositions(attributes.position, tileOptions); content.modelMatrix = enuMatrix.invert(); content.coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS; } else { content.modelMatrix = getModelMatrix(attributes.position); content.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS; } content.attributes = { positions: attributes.position, normals: attributes.normal, colors: normalizeAttribute(attributes.color), // Normalize from UInt8 texCoords: attributes.uv0, uvRegions: normalizeAttribute(attributes.uvRegion || attributes.region) // Normalize from UInt16 }; content.indices = indices || null; if (attributes.id && attributes.id.value) { content.featureIds = attributes.id.value; } for (const attributeIndex in content.attributes) { if (!content.attributes[attributeIndex]) { delete content.attributes[attributeIndex]; } } content.vertexCount = vertexCount; content.byteLength = contentByteLength; return content; } function updateAttributesMetadata(attributes, decompressedGeometry) { for (const key in decompressedGeometry.loaderData.attributes) { const dracoAttribute = decompressedGeometry.loaderData.attributes[key]; switch (dracoAttribute.name) { case "POSITION": attributes.position.metadata = dracoAttribute.metadata; break; case "feature-index": attributes.id.metadata = dracoAttribute.metadata; break; default: break; } } } function concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes) { return { ...normalizedVertexAttributes, ...normalizedFeatureAttributes }; } function normalizeAttribute(attribute) { if (!attribute) { return attribute; } attribute.normalized = true; return attribute; } function parseHeaders(arrayBuffer, options) { let byteOffset = 0; let vertexCount = 0; let featureCount = 0; for (const { property, type } of options.store.defaultGeometrySchema.header) { const TypedArrayTypeHeader = getConstructorForDataFormat(type); switch (property) { case HeaderAttributeProperty.vertexCount.toString(): vertexCount = new TypedArrayTypeHeader(arrayBuffer, 0, 4)[0]; byteOffset += sizeOf(type); break; case HeaderAttributeProperty.featureCount.toString(): featureCount = new TypedArrayTypeHeader(arrayBuffer, 4, 4)[0]; byteOffset += sizeOf(type); break; default: break; } } return { vertexCount, featureCount, byteOffset }; } function normalizeAttributes(arrayBuffer, byteOffset, vertexAttributes, attributeCount, attributesOrder) { const attributes = {}; for (const attribute of attributesOrder) { if (vertexAttributes[attribute]) { const { valueType, valuesPerElement } = vertexAttributes[attribute]; if (byteOffset + attributeCount * valuesPerElement * sizeOf(valueType) <= arrayBuffer.byteLength) { const buffer = arrayBuffer.slice(byteOffset); let value; if (valueType === "UInt64") { value = parseUint64Values(buffer, attributeCount * valuesPerElement, sizeOf(valueType)); } else { const TypedArrayType = getConstructorForDataFormat(valueType); value = new TypedArrayType(buffer, 0, attributeCount * valuesPerElement); } attributes[attribute] = { value, type: GL_TYPE_MAP[valueType], size: valuesPerElement }; switch (attribute) { case "color": attributes.color.normalized = true; break; case "position": case "region": case "normal": default: } byteOffset = byteOffset + attributeCount * valuesPerElement * sizeOf(valueType); } else if (attribute !== "uv0") { break; } } } return { attributes, byteOffset }; } function parseUint64Values(buffer, elementsCount, attributeSize) { const values = []; const dataView = new DataView(buffer); let offset = 0; for (let index = 0; index < elementsCount; index++) { const left = dataView.getUint32(offset, true); const right = dataView.getUint32(offset + 4, true); const value = left + 2 ** 32 * right; values.push(value); offset += attributeSize; } return new Uint32Array(values); } function parsePositions(attribute, options) { const mbs = options.mbs; const value = attribute.value; const metadata = attribute.metadata; const enuMatrix = new import_core2.Matrix4(); const cartographicOrigin = new import_core2.Vector3(mbs[0], mbs[1], mbs[2]); const cartesianOrigin = new import_core2.Vector3(); import_geospatial.Ellipsoid.WGS84.cartographicToCartesian(cartographicOrigin, cartesianOrigin); import_geospatial.Ellipsoid.WGS84.eastNorthUpToFixedFrame(cartesianOrigin, enuMatrix); attribute.value = offsetsToCartesians(value, metadata, cartographicOrigin); return enuMatrix; } function offsetsToCartesians(vertices, metadata = {}, cartographicOrigin) { const positions = new Float64Array(vertices.length); const scaleX = metadata["i3s-scale_x"] && metadata["i3s-scale_x"].double || 1; const scaleY = metadata["i3s-scale_y"] && metadata["i3s-scale_y"].double || 1; for (let i = 0; i < positions.length; i += 3) { positions[i] = vertices[i] * scaleX + cartographicOrigin.x; positions[i + 1] = vertices[i + 1] * scaleY + cartographicOrigin.y; positions[i + 2] = vertices[i + 2] + cartographicOrigin.z; } for (let i = 0; i < positions.length; i += 3) { import_geospatial.Ellipsoid.WGS84.cartographicToCartesian(positions.subarray(i, i + 3), scratchVector); positions[i] = scratchVector.x; positions[i + 1] = scratchVector.y; positions[i + 2] = scratchVector.z; } return positions; } function getModelMatrix(positions) { var _a, _b; const metadata = positions.metadata; const scaleX = ((_a = metadata == null ? void 0 : metadata["i3s-scale_x"]) == null ? void 0 : _a.double) || 1; const scaleY = ((_b = metadata == null ? void 0 : metadata["i3s-scale_y"]) == null ? void 0 : _b.double) || 1; const modelMatrix = new import_core2.Matrix4(); modelMatrix[0] = scaleX; modelMatrix[5] = scaleY; return modelMatrix; } function makePbrMaterial(materialDefinition, texture) { let pbrMaterial; if (materialDefinition) { pbrMaterial = { ...materialDefinition, pbrMetallicRoughness: materialDefinition.pbrMetallicRoughness ? { ...materialDefinition.pbrMetallicRoughness } : { baseColorFactor: [255, 255, 255, 255] } }; } else { pbrMaterial = { pbrMetallicRoughness: {} }; if (texture) { pbrMaterial.pbrMetallicRoughness.baseColorTexture = { texCoord: 0 }; } else { pbrMaterial.pbrMetallicRoughness.baseColorFactor = [255, 255, 255, 255]; } } pbrMaterial.alphaCutoff = pbrMaterial.alphaCutoff || 0.25; if (pbrMaterial.alphaMode) { pbrMaterial.alphaMode = pbrMaterial.alphaMode.toUpperCase(); } if (pbrMaterial.emissiveFactor) { pbrMaterial.emissiveFactor = convertColorFormat(pbrMaterial.emissiveFactor); } if (pbrMaterial.pbrMetallicRoughness && pbrMaterial.pbrMetallicRoughness.baseColorFactor) { pbrMaterial.pbrMetallicRoughness.baseColorFactor = convertColorFormat(pbrMaterial.pbrMetallicRoughness.baseColorFactor); } if (texture) { setMaterialTexture(pbrMaterial, texture); } return pbrMaterial; } function convertColorFormat(colorFactor) { const normalizedColor = [...colorFactor]; for (let index = 0; index < colorFactor.length; index++) { normalizedColor[index] = colorFactor[index] / 255; } return normalizedColor; } function setMaterialTexture(material, image) { const texture = { source: { image } }; if (material.pbrMetallicRoughness && material.pbrMetallicRoughness.baseColorTexture) { material.pbrMetallicRoughness.baseColorTexture = { ...material.pbrMetallicRoughness.baseColorTexture, texture }; } else if (material.emissiveTexture) { material.emissiveTexture = { ...material.emissiveTexture, texture }; } else if (material.pbrMetallicRoughness && material.pbrMetallicRoughness.metallicRoughnessTexture) { material.pbrMetallicRoughness.metallicRoughnessTexture = { ...material.pbrMetallicRoughness.metallicRoughnessTexture, texture }; } else if (material.normalTexture) { material.normalTexture = { ...material.normalTexture, texture }; } else if (material.occlusionTexture) { material.occlusionTexture = { ...material.occlusionTexture, texture }; } } function flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes) { const { id, faceRange } = normalizedFeatureAttributes; if (!id || !faceRange) { return; } const featureIds = id.value; const range = faceRange.value; const featureIdsLength = range[range.length - 1] + 1; const orderedFeatureIndices = new Uint32Array(featureIdsLength * 3); let featureIndex = 0; let startIndex = 0; for (let index = 1; index < range.length; index += 2) { const fillId = Number(featureIds[featureIndex]); const endValue = range[index]; const prevValue = range[index - 1]; const trianglesCount = endValue - prevValue + 1; const endIndex = startIndex + trianglesCount * 3; orderedFeatureIndices.fill(fillId, startIndex, endIndex); featureIndex++; startIndex = endIndex; } normalizedFeatureAttributes.id.value = orderedFeatureIndices; } function flattenFeatureIdsByFeatureIndices(attributes, featureIds) { const featureIndices = attributes.id.value; const result = new Float32Array(featureIndices.length); for (let index = 0; index < featureIndices.length; index++) { result[index] = featureIds[featureIndices[index]]; } attributes.id.value = result; } function getFeatureIdsFromFeatureIndexMetadata(featureIndex) { var _a, _b; return (_b = (_a = featureIndex == null ? void 0 : featureIndex.metadata) == null ? void 0 : _a["i3s-feature-ids"]) == null ? void 0 : _b.intArray; } // dist/i3s-content-loader.js var VERSION = true ? "4.3.2" : "latest"; var I3SContentLoader = { dataType: null, batchType: null, name: "I3S Content (Indexed Scene Layers)", id: "i3s-content", module: "i3s", worker: true, version: VERSION, mimeTypes: ["application/octet-stream"], parse: parse2, extensions: ["bin"], options: { "i3s-content": {} } }; async function parse2(data, options, context) { const { tile, _tileOptions, tileset, _tilesetOptions } = (options == null ? void 0 : options.i3s) || {}; const tileOptions = _tileOptions || tile; const tilesetOptions = _tilesetOptions || tileset; if (!tileOptions || !tilesetOptions) { return null; } return await parseI3STileContent(data, tileOptions, tilesetOptions, options, context); } // dist/lib/parsers/parse-i3s.js var import_culling = require("@math.gl/culling"); var import_geospatial2 = require("@math.gl/geospatial"); var import_core4 = require("@loaders.gl/core"); var import_tiles = require("@loaders.gl/tiles"); // dist/lib/helpers/i3s-nodepages-tiles.js var import_core3 = require("@loaders.gl/core"); var import_textures2 = require("@loaders.gl/textures"); // dist/i3s-node-page-loader.js var VERSION2 = true ? "4.3.2" : "latest"; var I3SNodePageLoader = { dataType: null, batchType: null, name: "I3S Node Page", id: "i3s-node-page", module: "i3s", version: VERSION2, mimeTypes: ["application/json"], parse: parseNodePage, extensions: ["json"], options: { i3s: {} } }; async function parseNodePage(data, options) { return JSON.parse(new TextDecoder().decode(data)); } // dist/lib/helpers/i3s-nodepages-tiles.js var I3SNodePagesTiles = class { tileset; nodePages = []; pendingNodePages = []; nodesPerPage; options; lodSelectionMetricType; textureDefinitionsSelectedFormats = []; nodesInNodePages; url; textureLoaderOptions = {}; /** * @constructs * Create a I3SNodePagesTiles instance. * @param tileset - i3s tileset header ('layers/0') * @param url - tileset url * @param options - i3s loader options */ constructor(tileset, url = "", options) { var _a, _b; this.tileset = { ...tileset }; this.url = url; this.nodesPerPage = ((_a = tileset.nodePages) == null ? void 0 : _a.nodesPerPage) || 64; this.lodSelectionMetricType = (_b = tileset.nodePages) == null ? void 0 : _b.lodSelectionMetricType; this.options = options; this.nodesInNodePages = 0; this.initSelectedFormatsForTextureDefinitions(tileset); } /** * Loads some nodePage and return a particular node from it * @param id - id of node through all node pages */ async getNodeById(id) { var _a; const pageIndex = Math.floor(id / this.nodesPerPage); if (!this.nodePages[pageIndex] && !this.pendingNodePages[pageIndex]) { const nodePageUrl = getUrlWithToken( `${this.url}/nodepages/${pageIndex}`, // @ts-expect-error this.options is not properly typed (_a = this.options.i3s) == null ? void 0 : _a.token ); this.pendingNodePages[pageIndex] = { status: "Pending", promise: (0, import_core3.load)(nodePageUrl, I3SNodePageLoader, this.options) }; this.nodePages[pageIndex] = await this.pendingNodePages[pageIndex].promise; this.nodesInNodePages += this.nodePages[pageIndex].nodes.length; this.pendingNodePages[pageIndex].status = "Done"; } if (this.pendingNodePages[pageIndex].status === "Pending") { this.nodePages[pageIndex] = await this.pendingNodePages[pageIndex].promise; } const nodeIndex = id % this.nodesPerPage; return this.nodePages[pageIndex].nodes[nodeIndex]; } /** * Forms tile header using node and tileset data * @param id - id of node through all node pages */ // eslint-disable-next-line complexity, max-statements async formTileFromNodePages(id) { const node = await this.getNodeById(id); const children = []; const childNodesPromises = []; for (const child of node.children || []) { childNodesPromises.push(this.getNodeById(child)); } const childNodes = await Promise.all(childNodesPromises); for (const childNode of childNodes) { children.push({ id: childNode.index.toString(), obb: childNode.obb }); } let contentUrl; let textureUrl; let materialDefinition; let textureFormat = "jpg"; let attributeUrls = []; let isDracoGeometry = false; if (node && node.mesh) { const { url, isDracoGeometry: isDracoGeometryResult } = node.mesh.geometry && this.getContentUrl(node.mesh.geometry) || { isDracoGeometry: false }; contentUrl = url; isDracoGeometry = isDracoGeometryResult; const { textureData, materialDefinition: nodeMaterialDefinition } = this.getInformationFromMaterial(node.mesh.material); materialDefinition = nodeMaterialDefinition; textureFormat = textureData.format || textureFormat; if (textureData.name) { textureUrl = `${this.url}/nodes/${node.mesh.material.resource}/textures/${textureData.name}`; } if (this.tileset.attributeStorageInfo) { attributeUrls = generateTilesetAttributeUrls(this.tileset, this.url, node.mesh.attribute.resource); } } const lodSelection = this.getLodSelection(node); return normalizeTileNonUrlData({ id: id.toString(), lodSelection, obb: node.obb, contentUrl, textureUrl, attributeUrls, materialDefinition, textureFormat, textureLoaderOptions: this.textureLoaderOptions, children, isDracoGeometry }); } /** * Forms url and type of geometry resource by nodepage's data and `geometryDefinitions` in the tileset * @param - data about the node's mesh from the nodepage * @returns - * {string} url - url to the geometry resource * {boolean} isDracoGeometry - whether the geometry resource contain DRACO compressed geometry */ getContentUrl(meshGeometryData) { let result = null; const geometryDefinition = this.tileset.geometryDefinitions[meshGeometryData.definition]; let geometryIndex = -1; if (this.options.i3s && this.options.i3s.useDracoGeometry) { geometryIndex = geometryDefinition.geometryBuffers.findIndex((buffer) => buffer.compressedAttributes && buffer.compressedAttributes.encoding === "draco"); } if (geometryIndex === -1) { geometryIndex = geometryDefinition.geometryBuffers.findIndex((buffer) => !buffer.compressedAttributes); } if (geometryIndex !== -1) { const isDracoGeometry = Boolean(geometryDefinition.geometryBuffers[geometryIndex].compressedAttributes); result = { url: `${this.url}/nodes/${meshGeometryData.resource}/geometries/${geometryIndex}`, isDracoGeometry }; } return result; } /** * Forms 1.6 compatible LOD selection object from a nodepage's node data * @param node - a node from nodepage * @returns- Array of LodSelection */ getLodSelection(node) { const lodSelection = []; if (this.lodSelectionMetricType === "maxScreenThresholdSQ") { lodSelection.push({ metricType: "maxScreenThreshold", // @ts-ignore maxError: Math.sqrt(node.lodThreshold / (Math.PI * 0.25)) }); } lodSelection.push({ metricType: this.lodSelectionMetricType, // @ts-ignore maxError: node.lodThreshold }); return lodSelection; } /** * Returns information about texture and material from `materialDefinitions` * @param material - material data from nodepage * @returns - Couple {textureData, materialDefinition} * {string} textureData.name - path name of the texture * {string} textureData.format - format of the texture * materialDefinition - PBR-like material definition from `materialDefinitions` */ getInformationFromMaterial(material) { var _a, _b, _c; const informationFromMaterial = { textureData: { name: null } }; if (material) { const materialDefinition = (_a = this.tileset.materialDefinitions) == null ? void 0 : _a[material.definition]; if (materialDefinition) { informationFromMaterial.materialDefinition = materialDefinition; const textureSetDefinitionIndex = (_c = (_b = materialDefinition == null ? void 0 : materialDefinition.pbrMetallicRoughness) == null ? void 0 : _b.baseColorTexture) == null ? void 0 : _c.textureSetDefinitionId; if (typeof textureSetDefinitionIndex === "number") { informationFromMaterial.textureData = this.textureDefinitionsSelectedFormats[textureSetDefinitionIndex] || informationFromMaterial.textureData; } } } return informationFromMaterial; } /** * Sets preferable and supported format for each textureDefinition of the tileset * @param tileset - I3S layer data * @returns */ initSelectedFormatsForTextureDefinitions(tileset) { this.textureDefinitionsSelectedFormats = []; const possibleI3sFormats = this.getSupportedTextureFormats(); const textureSetDefinitions = tileset.textureSetDefinitions || []; for (const textureSetDefinition of textureSetDefinitions) { const formats = textureSetDefinition && textureSetDefinition.formats || []; let selectedFormat = null; for (const i3sFormat of possibleI3sFormats) { const format = formats.find((value) => value.format === i3sFormat); if (format) { selectedFormat = format; break; } } if (selectedFormat && selectedFormat.format === "ktx2") { this.textureLoaderOptions.basis = { format: (0, import_textures2.selectSupportedBasisFormat)(), containerFormat: "ktx2", module: "encoder" }; } this.textureDefinitionsSelectedFormats.push(selectedFormat); } } /** * Returns the array of supported texture format * @returns list of format strings */ getSupportedTextureFormats() { const formats = []; if (!this.options.i3s || this.options.i3s.useCompressedTextures) { const supportedCompressedFormats = (0, import_textures2.getSupportedGPUTextureFormats)(); if (supportedCompressedFormats.has("etc2")) { formats.push("ktx-etc2"); } if (supportedCompressedFormats.has("dxt")) { formats.push("dds"); } formats.push("ktx2"); } formats.push("jpg"); formats.push("png"); return formats; } }; // dist/lib/parsers/parse-i3s.js function normalizeTileData(tile, context) { const url = context.url || ""; let contentUrl; if (tile.geometryData) { contentUrl = `${url}/${tile.geometryData[0].href}`; } let textureUrl; if (tile.textureData) { textureUrl = `${url}/${tile.textureData[0].href}`; } let attributeUrls; if (tile.attributeData) { attributeUrls = generateTileAttributeUrls(url, tile); } const children = tile.children || []; return normalizeTileNonUrlData({ ...tile, children, url, contentUrl, textureUrl, textureFormat: "jpg", // `jpg` format will cause `ImageLoader` usage that will be able to handle `png` as well attributeUrls, isDracoGeometry: false }); } function normalizeTileNonUrlData(tile) { var _a, _b; const boundingVolume = {}; let mbs = [0, 0, 0, 1]; if (tile.mbs) { mbs = tile.mbs; boundingVolume.sphere = [ ...import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(tile.mbs.slice(0, 3)), // cartesian center of sphere tile.mbs[3] // radius of sphere ]; } else if (tile.obb) { boundingVolume.box = [ ...import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(tile.obb.center), // cartesian center of box ...tile.obb.halfSize, // halfSize ...tile.obb.quaternion // quaternion ]; const obb = new import_culling.OrientedBoundingBox().fromCenterHalfSizeQuaternion(boundingVolume.box.slice(0, 3), tile.obb.halfSize, tile.obb.quaternion); const boundingSphere = obb.getBoundingSphere(); boundingVolume.sphere = [...boundingSphere.center, boundingSphere.radius]; mbs = [...tile.obb.center, boundingSphere.radius]; } const lodMetricType = (_a = tile.lodSelection) == null ? void 0 : _a[0].metricType; const lodMetricValue = (_b = tile.lodSelection) == null ? void 0 : _b[0].maxError; const type = import_tiles.TILE_TYPE.MESH; const refine = import_tiles.TILE_REFINEMENT.REPLACE; return { ...tile, mbs, boundingVolume, lodMetricType, lodMetricValue, type, refine }; } async function normalizeTilesetData(tileset, options, context) { const url = getUrlWithoutParams(context.url || ""); let nodePagesTile; let root; if (tileset.nodePages) { nodePagesTile = new I3SNodePagesTiles(tileset, url, options); root = await nodePagesTile.formTileFromNodePages(0); } else { const parseOptions = options.i3s; const rootNodeUrl = getUrlWithToken(`${url}/nodes/root`, parseOptions.token); root = await (0, import_core4.load)(rootNodeUrl, I3SLoader, { ...options, i3s: { // @ts-expect-error options is not properly typed ...options.i3s, loadContent: false, isTileHeader: true, isTileset: false } }); } return { ...tileset, loader: I3SLoader, url, basePath: url, type: import_tiles.TILESET_TYPE.I3S, nodePagesTile, // @ts-expect-error root, lodMetricType: root.lodMetricType, lodMetricValue: root.lodMetricValue }; } // dist/i3s-loader.js var VERSION3 = true ? "4.3.2" : "latest"; var TILESET_REGEX = /layers\/[0-9]+$/; var LOCAL_SLPK_REGEX = /\.slpk$/; var TILE_HEADER_REGEX = /nodes\/([0-9-]+|root)$/; var SLPK_HEX = "504b0304"; var POINT_CLOUD = "PointCloud"; var I3SLoader = { dataType: null, batchType: null, name: "I3S (Indexed Scene Layers)", id: "i3s", module: "i3s", version: VERSION3, mimeTypes: ["application/octet-stream"], parse: parseI3S, extensions: ["bin"], options: { i3s: { token: null, isTileset: "auto", isTileHeader: "auto", tile: null, tileset: null, _tileOptions: null, _tilesetOptions: null, useDracoGeometry: true, useCompressedTextures: true, decodeTextures: true, coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS } } }; async function parseI3S(data, options = {}, context) { const url = context.url; options.i3s = options.i3s || {}; const magicNumber = getMagicNumber(data); if (magicNumber === SLPK_HEX) { throw new Error("Files with .slpk extention currently are not supported by I3SLoader"); } const urlWithoutParams = getUrlWithoutParams(url); let isTileset; if (options.i3s.isTileset === "auto") { isTileset = TILESET_REGEX.test(urlWithoutParams) || LOCAL_SLPK_REGEX.test(urlWithoutParams); } else { isTileset = options.i3s.isTileset; } let isTileHeader; if (options.isTileHeader === "auto") { isTileHeader = TILE_HEADER_REGEX.test(urlWithoutParams); } else { isTileHeader = options.i3s.isTileHeader; } if (isTileset) { data = await parseTileset(data, options, context); } else if (isTileHeader) { data = await parseTile(data, context); } else { data = await parseTileContent(data, options); } return data; } async function parseTileContent(arrayBuffer, options) { return await (0, import_core5.parse)(arrayBuffer, I3SContentLoader, options); } async function parseTileset(data, options, context) { const tilesetJson = JSON.parse(new TextDecoder().decode(data)); if ((tilesetJson == null ? void 0 : tilesetJson.layerType) === POINT_CLOUD) { throw new Error("Point Cloud layers currently are not supported by I3SLoader"); } const tilesetPostprocessed = await normalizeTilesetData(tilesetJson, options, context); return tilesetPostprocessed; } async function parseTile(data, context) { data = JSON.parse(new TextDecoder().decode(data)); return normalizeTileData(data, context); } function getMagicNumber(data) { if (data instanceof ArrayBuffer) { return [...new Uint8Array(data, 0, 4)].map((value) => value.toString(16).padStart(2, "0")).join(""); } return null; } // dist/i3s-slpk-loader.js var import_loader_utils2 = require("@loaders.gl/loader-utils"); // dist/lib/parsers/parse-slpk/parse-slpk.js var import_zip2 = require("@loaders.gl/zip"); // dist/lib/parsers/parse-slpk/slpk-archieve.js var import_crypto = require("@loaders.gl/crypto"); var import_zip = require("@loaders.gl/zip"); var import_compression = require("@loaders.gl/compression"); var PATH_DESCRIPTIONS = [ { test: /^$/, extensions: ["3dSceneLayer.json.gz"] }, { test: /nodepages\/\d+$/, extensions: [".json.gz"] }, { test: /sublayers\/\d+$/, extensions: ["/3dSceneLayer.json.gz"] }, { test: /nodes\/(\d+|root)$/, extensions: ["/3dNodeIndexDocument.json.gz"] }, { test: /nodes\/\d+\/textures\/.+$/, extensions: [".jpg", ".png", ".bin.dds.gz", ".ktx", ".ktx2"] }, { test: /nodes\/\d+\/geometries\/\d+$/, extensions: [".bin.gz", ".draco.gz"] }, { test: /nodes\/\d+\/attributes\/f_\d+\/\d+$/, extensions: [".bin.gz"] }, { test: /statistics\/(f_\d+\/\d+|summary)$/, extensions: [".json.gz"] }, { test: /nodes\/\d+\/shared$/, extensions: ["/sharedResource.json.gz"] } ]; var SLPKArchive = class extends import_zip.IndexedArchive { // Maps hex-encoded md5 filename hashes to bigint offsets into the archive hashTable; _textEncoder = new TextEncoder(); _textDecoder = new TextDecoder(); _md5Hash = new import_crypto.MD5Hash(); /** * Constructor * @param fileProvider - instance of a binary data reader * @param hashTable - pre-loaded hashTable. If presented, getFile will skip reading the hash file * @param fileName - name of the archive. It is used to add to an URL of a loader context */ constructor(fileProvider, hashTable, fileName) { super(fileProvider, hashTable, fileName); this.hashTable = hashTable; } /** * Returns file with the given path from slpk archive * @param path - path inside the slpk * @param mode - currently only raw mode supported * @returns buffer with ready to use file */ async getFile(path, mode = "raw") { var _a; if (mode === "http") { const extensions = (_a = PATH_DESCRIPTIONS.find((val) => val.test.test(path))) == null ? void 0 : _a.extensions; if (extensions) { let data; for (const ext of extensions) { data = await this.getDataByPath(`${path}${ext}`); if (data) { break; } } if (data) { return data; } } } if (mode === "raw") { const decompressedFile = await this.getDataByPath(`${path}.gz`); if (decompressedFile) { return decompressedFile; } const fileWithoutCompression = await this.getFileBytes(path); if (fileWithoutCompression) { return fileWithoutCompression; } } throw new Error(`No such file in the archive: ${path}`); } /** * returning uncompressed data for paths that ends with .gz and raw data for all other paths * @param path - path inside the archive * @returns buffer with the file data */ async getDataByPath(path) { let data = await this.getFileBytes(path.toLocaleLowerCase()); if (!data) { data = await this.getFileBytes(path); } if (!data) { return void 0; } if (/\.gz$/.test(path)) { const compression = new import_compression.GZipCompression(); const decompressedData = await compression.decompress(data); return decompressedData; } return data; } /** * Trying to get raw file data by address * @param path - path inside the archive * @returns buffer with the raw file data */ async getFileBytes(path) { let compressedFile; if (this.hashTable) { const binaryPath = this._textEncoder.encode(path); const nameHash = await this._md5Hash.hash(binaryPath.buffer, "hex"); const offset = this.hashTable[nameHash]; if (offset === void 0) { return void 0; } const localFileHeader = await (0, import_zip.parseZipLocalFileHeader)(offset, this.fileProvider); if (!localFileHeader) { return void 0; } compressedFile = await this.fileProvider.slice(localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize); } else { try { compressedFile = await this.getFileWithoutHash(path); } catch { compressedFile = void 0; } } return compressedFile; } }; // dist/lib/parsers/parse-slpk/parse-slpk.js async function parseSLPKArchive(fileProvider, cb, fileName) { const hashCDOffset = await (0, import_zip2.searchFromTheEnd)(fileProvider, import_zip2.CD_HEADER_SIGNATURE); const cdFileHeader = await (0, import_zip2.parseZipCDFileHeader)(hashCDOffset, fileProvider); let hashTable; if ((cdFileHeader == null ? void 0 : cdFileHeader.fileName) !== "@specialIndexFileHASH128@") { hashTable = await (0, import_zip2.makeHashTableFromZipHeaders)(fileProvider); cb == null ? void 0 : cb("SLPK doesnt contain hash file, hash info has been composed according to zip archive headers"); } else { const localFileHeader = await (0, import_zip2.parseZipLocalFileHeader)(cdFileHeader.localHeaderOffset, fileProvider); if (!localFileHeader) { throw new Error("corrupted SLPK"); } const fileDataOffset = localFileHeader.fileDataOffset; const hashFile = await fileProvider.slice(fileDataOffset, fileDataOffset + localFileHeader.compressedSize); hashTable = (0, import_zip2.parseHashTable)(hashFile); } return new SLPKArchive(fileProvider, hashTable, fileName); } // dist/i3s-slpk-loader.js var VERSION4 = true ? "4.3.2" : "latest"; var SLPKLoader = { dataType: null, batchType: null, name: "I3S SLPK (Scene Layer Package)", id: "slpk", module: "i3s", version: VERSION4, mimeTypes: ["application/octet-stream"], extensions: ["slpk"], options: {}, parse: async (data, options = {}) => { var _a, _b; const archive = await parseSLPKArchive(new import_loader_utils2.DataViewFile(new DataView(data))); return archive.getFile(((_a = options.slpk) == null ? void 0 : _a.path) ?? "", (_b = options.slpk) == null ? void 0 : _b.pathMode); } }; // dist/i3s-attribute-loader.js var import_core6 = require("@loaders.gl/core"); // dist/lib/parsers/parse-i3s-attribute.js function parseI3STileAttribute(arrayBuffer, options) { const { attributeName, attributeType } = options; if (!attributeName) { return {}; } return { [attributeName]: attributeType ? parseAttribute(attributeType, arrayBuffer) : null }; } function parseAttribute(attributeType, arrayBuffer) { switch (attributeType) { case STRING_ATTRIBUTE_TYPE: return parseStringsAttribute(arrayBuffer); case OBJECT_ID_ATTRIBUTE_TYPE: return parseShortNumberAttribute(arrayBuffer); case FLOAT_64_TYPE: return parseFloatAttribute(arrayBuffer); case INT_16_ATTRIBUTE_TYPE: return parseInt16ShortNumberAttribute(arrayBuffer); default: return parseShortNumberAttribute(arrayBuffer); } } function parseShortNumberAttribute(arrayBuffer) { const countOffset = 4; return new Uint32Array(arrayBuffer, countOffset); } function parseInt16ShortNumberAttribute(arrayBuffer) { const countOffset = 4; return new Int16Array(arrayBuffer, countOffset); } function parseFloatAttribute(arrayBuffer) { const countOffset = 8; return new Float64Array(arrayBuffer, countOffset); } function parseStringsAttribute(arrayBuffer) { const stringsCountOffset = 0; const dataOffset = 8; const bytesPerStringSize = 4; const stringsArray = []; try { const stringsCount = new DataView(arrayBuffer, stringsCountOffset, bytesPerStringSize).getUint32(stringsCountOffset, true); const stringSizes = new Uint32Array(arrayBuffer, dataOffset, stringsCount); let stringOffset = dataOffset + stringsCount * bytesPerStringSize; for (const stringByteSize of stringSizes) { const textDecoder = new TextDecoder("utf-8"); const stringAttribute = new Uint8Array(arrayBuffer, stringOffset, stringByteSize); stringsArray.push(textDecoder.decode(stringAttribute)); stringOffset += stringByteSize; } } catch (error) { console.error("Parse string attribute error: ", error.message); } return stringsArray; } // dist/i3s-attribute-loader.js var VERSION5 = true ? "4.3.2" : "latest"; var EMPTY_VALUE = ""; var REJECTED_STATUS = "rejected"; var I3SAttributeLoader = { dataType: null, batchType: null, name: "I3S Attribute", id: "i3s-attribute", module: "i3s", version: VERSION5, mimeTypes: ["application/binary"], parse: async (arrayBuffer, options) => parseI3STileAttribute(arrayBuffer, options), extensions: ["bin"], options: {}, binary: true }; async function loadFeatureAttributes(tile, featureId, options = {}) { var _a; const { attributeStorageInfo, attributeUrls, tilesetFields } = getAttributesData(tile); if (!attributeStorageInfo || !attributeUrls || featureId < 0) { return null; } let attributes = []; const attributeLoadPromises = []; for (let index = 0; index < attributeStorageInfo.length; index++) { const url = getUrlWithToken(attributeUrls[index], (_a = options.i3s) == null ? void 0 : _a.token); const attributeName = attributeStorageInfo[index].name; const attributeType = getAttributeValueType(attributeStorageInfo[index]); const loadOptions = { ...options, attributeName, attributeType }; const promise = (0, import_core6.load)(url, I3SAttributeLoader, loadOptions); attributeLoadPromises.push(promise); } try { attributes = await Promise.allSettled(attributeLoadPromises); } catch (error) { } if (!attributes.length) { return null; } return generateAttributesByFeatureId(attributes, attributeStorageInfo, featureId, tilesetFields); } function getAttributesData(tile) { var _a, _b, _c, _d, _e; const attributeStorageInfo = (_b = (_a = tile.tileset) == null ? void 0 : _a.tileset) == null ? void 0 : _b.attributeStorageInfo; const attributeUrls = (_c = tile.header) == null ? void 0 : _c.attributeUrls; const tilesetFields = ((_e = (_d = tile.tileset) == null ? void 0 : _d.tileset) == null ? void 0 : _e.fields) || []; return { attributeStorageInfo, attributeUrls, tilesetFields }; } function getAttributeValueType(attribute) { if (attribute.hasOwnProperty("objectIds")) { return "Oid32"; } else if (attribute.hasOwnProperty("attributeValues")) { return attribute.attributeValues.valueType; } return ""; } function getFeatureIdsAttributeName(attributeStorageInfo) { const objectIdsAttribute = attributeStorageInfo.find((attribute) => attribute.name.includes("OBJECTID")); return objectIdsAttribute == null ? void 0 : objectIdsAttribute.name; } function generateAttributesByFeatureId(attributes, attributeStorageInfo, featureId, tilesetFields) { const objectIdsAttributeName = getFeatureIdsAttributeName(attributeStorageInfo); const objectIds = attributes.find((attribute) => attribute.value[objectIdsAttributeName]); if (!objectIds) { return null; } const attributeIndex = objectIds.value[objectIdsAttributeName].indexOf(featureId); if (attributeIndex < 0) { return null; } return getFeatureAttributesByIndex(attributes, attributeIndex, attributeStorageInfo, tilesetFields); } function getFeatureAttributesByIndex(attributes, featureIdIndex, attributeStorageInfo, tilesetFields) { const attributesObject = {}; for (let index = 0; index < attributeStorageInfo.length; index++) { const attributeName = attributeStorageInfo[index].name; const codedValues = getAttributeCodedValues(attributeName, tilesetFields); const attribute = getAttributeByIndexAndAttributeName(attributes, index, attributeName); attributesObject[attributeName] = formatAttributeValue(attribute, featureIdIndex, codedValues); } return attributesObject; } function getAttributeCodedValues(attributeName, tilesetFields) { var _a; const attributeField = tilesetFields.find((field) => field.name === attributeName || field.alias === attributeName); return ((_a = attributeField == null ? void 0 : attributeField.domain) == null ? void 0 : _a.codedValues) || []; } function getAttributeByIndexAndAttributeName(attributes, index, attributesName) { const attributeObject = attributes[index]; if (attributeObject.status === REJECTED_STATUS) { return null; } return attributeObject.value[attributesName]; } function formatAttributeValue(attribute, featureIdIndex, codedValues) { let value = EMPTY_VALUE; if (attribute && featureIdIndex in attribute) { value = String(attribute[featureIdIndex]).replace(/\u0000|NaN/g, "").trim(); } if (codedValues.length) { const codeValue = codedValues.find((codedValue) => codedValue.code === Number(value)); value = (codeValue == null ? void 0 : codeValue.name) || EMPTY_VALUE; } return value; } // dist/lib/parsers/parse-i3s-building-scene-layer.js var OBJECT_3D_LAYER_TYPE = "3DObject"; async function parseBuildingSceneLayer(data, url) { const layer0 = JSON.parse(new TextDecoder().decode(data)); const { sublayers } = layer0; return { header: layer0, sublayers: parseSublayersTree(sublayers, url) }; } function parseSublayersTree(sublayers, url) { var _a; let layers = []; for (let index = 0; index < sublayers.length; index++) { const subLayer = sublayers[index]; const { id, layerType, visibility = true, ...rest } = subLayer; if (layerType === OBJECT_3D_LAYER_TYPE) { const sublayerUrl = `${url}/sublayers/${id}`; layers.push({ url: sublayerUrl, id, layerType, visibility, ...rest }); } if ((_a = subLayer == null ? void 0 : subLayer.sublayers) == null ? void 0 : _a.length) { layers = [...layers, ...p