UNPKG

@itwin/core-frontend

Version:
176 lines • 11.6 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.ThreeDTileFormatInterpreter = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const core_geometry_1 = require("@itwin/core-geometry"); const FrontendLoggerCategory_1 = require("../../common/FrontendLoggerCategory"); const RealityDataSource_1 = require("../../RealityDataSource"); const loggerCategory = FrontendLoggerCategory_1.FrontendLoggerCategory.RealityData; /** * This class provide methods used to interpret Cesium 3dTile format */ class ThreeDTileFormatInterpreter { /** Gets reality data spatial location and extents * @param json root document file in json format * @returns spatial location and volume of interest, in meters, centered around `spatial location` * @throws [[RealityDataError]] if source is invalid or cannot be read */ static getSpatialLocationAndExtents(json) { const worldRange = new core_geometry_1.Range3d(); let isGeolocated = true; let location; core_bentley_1.Logger.logTrace(loggerCategory, "RealityData getSpatialLocationAndExtents"); if (undefined === json?.root) { core_bentley_1.Logger.logWarning(loggerCategory, `Error getSpatialLocationAndExtents - no root in json`); // return first 1024 char from the json const getMetaData = () => { return { json: JSON.stringify(json).substring(0, 1024) }; }; const error = new RealityDataSource_1.RealityDataError(core_bentley_1.RealityDataStatus.InvalidData, "Invalid or unknown data - no root in json", getMetaData); throw error; } try { if (undefined !== json?.root?.boundingVolume?.region) { const region = core_bentley_1.JsonUtils.asArray(json.root.boundingVolume.region); core_bentley_1.Logger.logTrace(loggerCategory, "RealityData json.root.boundingVolume.region", () => ({ ...region })); if (undefined === region) { core_bentley_1.Logger.logError(loggerCategory, `Error getSpatialLocationAndExtents - region undefined`); throw new TypeError("Unable to determine GeoLocation - no root Transform or Region on root."); } const ecefLow = (core_common_1.Cartographic.fromRadians({ longitude: region[0], latitude: region[1], height: region[4] })).toEcef(); const ecefHigh = (core_common_1.Cartographic.fromRadians({ longitude: region[2], latitude: region[3], height: region[5] })).toEcef(); const ecefRange = core_geometry_1.Range3d.create(ecefLow, ecefHigh); const cartoCenter = core_common_1.Cartographic.fromRadians({ longitude: (region[0] + region[2]) / 2.0, latitude: (region[1] + region[3]) / 2.0, height: (region[4] + region[5]) / 2.0 }); location = cartoCenter; const ecefLocation = core_common_1.EcefLocation.createFromCartographicOrigin(cartoCenter); // iModelDb.setEcefLocation(ecefLocation); const ecefToWorld = (0, core_bentley_1.expectDefined)(ecefLocation.getTransform().inverse()); worldRange.extendRange(core_geometry_1.Range3d.fromJSON(ecefToWorld.multiplyRange(ecefRange))); } else { let worldToEcefTransform = ThreeDTileFormatInterpreter.transformFromJson(json.root.transform); core_bentley_1.Logger.logTrace(loggerCategory, "RealityData json.root.transform", () => ({ ...worldToEcefTransform })); const range = (0, core_bentley_1.expectDefined)(ThreeDTileFormatInterpreter.rangeFromBoundingVolume(json.root.boundingVolume)); if (undefined === worldToEcefTransform) worldToEcefTransform = core_geometry_1.Transform.createIdentity(); const ecefRange = worldToEcefTransform.multiplyRange(range); // range in model -> range in ecef const ecefCenter = worldToEcefTransform.multiplyPoint3d(range.center); // range center in model -> range center in ecef const cartoCenter = core_common_1.Cartographic.fromEcef(ecefCenter); // ecef center to cartographic center const isNotNearEarthSurface = cartoCenter && (cartoCenter.height < -5000); // 5 km under ground! const earthCenterToRangeCenterRayLenght = range.center.magnitude(); if (worldToEcefTransform.matrix.isIdentity && (earthCenterToRangeCenterRayLenght < 1.0E5 || isNotNearEarthSurface)) { isGeolocated = false; worldRange.extendRange(core_geometry_1.Range3d.fromJSON(ecefRange)); const centerOfEarth = new core_common_1.EcefLocation({ origin: { x: 0.0, y: 0.0, z: 0.0 }, orientation: { yaw: 0.0, pitch: 0.0, roll: 0.0 } }); location = centerOfEarth; core_bentley_1.Logger.logTrace(loggerCategory, "RealityData NOT Geolocated", () => ({ ...location })); } else { let ecefLocation = core_common_1.EcefLocation.createFromTransform(worldToEcefTransform); // Fix Bug 445630: [RDV][Regression] Orientation of georeferenced Reality Mesh is wrong. // Use json.root.transform only if defined and not identity -> otherwise will use a transform computed from cartographic center. if (worldToEcefTransform.matrix.isIdentity) { // For georeferenced Reality Meshes, its origin is translated to model origin (0,0,0). // Apply range center to translate it back to its original position. const worldCenter = !worldToEcefTransform.matrix.isIdentity ? range.center : undefined; if (cartoCenter) ecefLocation = core_common_1.EcefLocation.createFromCartographicOrigin(cartoCenter, worldCenter); } location = ecefLocation; core_bentley_1.Logger.logTrace(loggerCategory, "RealityData is worldToEcefTransform.matrix.isIdentity", () => ({ isIdentity: worldToEcefTransform.matrix.isIdentity })); // iModelDb.setEcefLocation(ecefLocation); const ecefToWorld = (0, core_bentley_1.expectDefined)(ecefLocation.getTransform().inverse()); worldRange.extendRange(core_geometry_1.Range3d.fromJSON(ecefToWorld.multiplyRange(ecefRange))); core_bentley_1.Logger.logTrace(loggerCategory, "RealityData ecefToWorld", () => ({ ...ecefToWorld })); } } } catch { core_bentley_1.Logger.logWarning(loggerCategory, `Error getSpatialLocationAndExtents - cannot interpret json`); // return first 1024 char from the json const getMetaData = () => { return { json: JSON.stringify(json).substring(0, 1024) }; }; const error = new RealityDataSource_1.RealityDataError(core_bentley_1.RealityDataStatus.InvalidData, "Invalid or unknown data", getMetaData); throw error; } const spatialLocation = { location, worldRange, isGeolocated }; return spatialLocation; } /** Gets information to identify the product and engine that create this reality data * Will return undefined if cannot be resolved * @param rootDocjson root document file in json format * @returns information to identify the product and engine that create this reality data */ static getPublisherProductInfo(rootDocjson) { const info = { product: "", engine: "", version: "" }; if (rootDocjson && rootDocjson.root) { if (rootDocjson.root.SMPublisherInfo) { info.product = rootDocjson.root.SMPublisherInfo.Product ? rootDocjson.root.SMPublisherInfo.Product : ""; info.engine = rootDocjson.root.SMPublisherInfo.Publisher ? rootDocjson.root.SMPublisherInfo.Publisher : ""; info.version = rootDocjson.root.SMPublisherInfo["Publisher Version"] ? rootDocjson.root.SMPublisherInfo["Publisher Version"] : ""; } } return info; } /** Gets information about 3dTile file for this reality data * Will return undefined if cannot be resolved * @param rootDocjson root document file in json format * @returns information about 3dTile file for this reality data */ static getFileInfo(rootDocjson) { const info = { rootChildren: rootDocjson?.root?.children?.length ?? 0, }; return info; } /** Convert a boundingVolume into a range * @param boundingVolume the bounding volume to convert * @returns the range or undefined if cannot convert */ static rangeFromBoundingVolume(boundingVolume) { if (undefined === boundingVolume) return undefined; if (Array.isArray(boundingVolume.box)) { const box = boundingVolume.box; const center = core_geometry_1.Point3d.create(box[0], box[1], box[2]); const ux = core_geometry_1.Vector3d.create(box[3], box[4], box[5]); const uy = core_geometry_1.Vector3d.create(box[6], box[7], box[8]); const uz = core_geometry_1.Vector3d.create(box[9], box[10], box[11]); const corners = []; for (let j = 0; j < 2; j++) { for (let k = 0; k < 2; k++) { for (let l = 0; l < 2; l++) { corners.push(center.plus3Scaled(ux, (j ? -1.0 : 1.0), uy, (k ? -1.0 : 1.0), uz, (l ? -1.0 : 1.0))); } } } return core_geometry_1.Range3d.createArray(corners); } else if (Array.isArray(boundingVolume.sphere)) { const sphere = boundingVolume.sphere; const center = core_geometry_1.Point3d.create(sphere[0], sphere[1], sphere[2]); const radius = sphere[3]; return core_geometry_1.Range3d.createXYZXYZ(center.x - radius, center.y - radius, center.z - radius, center.x + radius, center.y + radius, center.z + radius); } return undefined; } /** Convert a boundingVolume into a range */ static maximumSizeFromGeometricTolerance(range, geometricError) { const minToleranceRatio = .5; // Nominally the error on screen size of a tile. Increasing generally increases performance (fewer draw calls) at expense of higher load times. return minToleranceRatio * range.diagonal().magnitude() / geometricError; } /** Convert a boundingVolume into a range */ static transformFromJson(jTrans) { return (jTrans === undefined) ? undefined : core_geometry_1.Transform.createOriginAndMatrix(core_geometry_1.Point3d.create(jTrans[12], jTrans[13], jTrans[14]), core_geometry_1.Matrix3d.createRowValues(jTrans[0], jTrans[4], jTrans[8], jTrans[1], jTrans[5], jTrans[9], jTrans[2], jTrans[6], jTrans[10])); } } exports.ThreeDTileFormatInterpreter = ThreeDTileFormatInterpreter; //# sourceMappingURL=ThreeDTileFormatInterpreter.js.map