@itwin/core-frontend
Version:
iTwin.js frontend components
176 lines • 11.6 kB
JavaScript
"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