import * as fs from "fs" ; import { Id64 , Id64String , JsonUtils } from "@itwin/core-bentley" ; import { Matrix3d , Point3d , Range3d , StandardViewIndex , Transform , Vector3d } from "@itwin/core-geometry" ; import { CategorySelector , DefinitionModel , DisplayStyle3d , IModelDb , ModelSelector , OrthographicViewDefinition , PhysicalModel , SnapshotDb , } from "@itwin/core-backend" ; import { AxisAlignedBox3d , Cartographic , ContextRealityModelProps , EcefLocation , RenderMode , ViewFlags } from "@itwin/core-common" ; class RealityModelTileUtils { public static rangeFromBoundingVolume ( boundingVolume ) { if ( undefined === boundingVolume ) return undefined ; if ( Array . isArray ( boundingVolume . box ) ) { const box = boundingVolume . box ; const center = Point3d . create ( box [ 0 ] , box [ 1 ] , box [ 2 ] ) ; const ux = Vector3d . create ( box [ 3 ] , box [ 4 ] , box [ 5 ] ) ; const uy = Vector3d . create ( box [ 6 ] , box [ 7 ] , box [ 8 ] ) ; const uz = 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 Range3d . createArray ( corners ) ; } else if ( Array . isArray ( boundingVolume . sphere ) ) { const sphere = boundingVolume . sphere ; const center = Point3d . create ( sphere [ 0 ] , sphere [ 1 ] , sphere [ 2 ] ) ; const radius = sphere [ 3 ] ; return Range3d . createXYZXYZ ( center . x - radius , center . y - radius , center . z - radius , center . x + radius , center . y + radius , center . z + radius ) ; } return undefined ; } public static maximumSizeFromGeometricTolerance ( range , geometricError ) { const minToleranceRatio = .5 ; return minToleranceRatio * range . diagonal ( ) . magnitude ( ) / geometricError ; } public static transformFromJson ( jTrans ) { return ( jTrans === undefined ) ? undefined : Transform . createOriginAndMatrix ( Point3d . create ( jTrans [ 12 ] , jTrans [ 13 ] , jTrans [ 14 ] ) , Matrix3d . createRowValues ( jTrans [ 0 ] , jTrans [ 4 ] , jTrans [ 8 ] , jTrans [ 1 ] , jTrans [ 5 ] , jTrans [ 9 ] , jTrans [ 2 ] , jTrans [ 6 ] , jTrans [ 10 ] ) ) ; } } /** */ export class RealityModelContextIModelCreator { public iModelDb : IModelDb ; public url : string ; public definitionModelId : Id64String = Id64 . invalid ; public physicalModelId : Id64String = Id64 . invalid ; param iModelFileName param url public constructor ( iModelFileName , url , private _name ) { fs . unlink ( iModelFileName , ( ( _err ) => { } ) ) ; this . iModelDb = SnapshotDb . createEmpty ( iModelFileName , { rootSubject : { name : "Reality Model Context" } } ) ; this . url = url ; } private realityModelFromJson ( json , worldRange ) { let geoLocated = true ; if ( undefined !== json . root . boundingVolume . region ) { const region = JsonUtils . asArray ( json . root . boundingVolume . region ) ; if ( undefined === region ) throw new TypeError ( "Unable to determine GeoLocation - no root Transform or Region on root." ) ; const ecefLow = ( Cartographic . fromRadians ( { longitude : region [ 0 ] , latitude : region [ 1 ] , height : region [ 4 ] } ) ) . toEcef ( ) ; const ecefHigh = ( Cartographic . fromRadians ( { longitude : region [ 2 ] , latitude : region [ 3 ] , height : region [ 5 ] } ) ) . toEcef ( ) ; const ecefRange = Range3d . create ( ecefLow , ecefHigh ) ; const cartoCenter = Cartographic . fromRadians ( { longitude : ( region [ 0 ] + region [ 2 ] ) / 2.0 , latitude : ( region [ 1 ] + region [ 3 ] ) / 2.0 , height : ( region [ 4 ] + region [ 5 ] ) / 2.0 } ) ; const ecefLocation = EcefLocation . createFromCartographicOrigin ( cartoCenter ) ; this . iModelDb . setEcefLocation ( ecefLocation ) ; const ecefToWorld = ecefLocation . getTransform ( ) . inverse ( ) ! ; worldRange . extendRange ( Range3d . fromJSON ( ecefToWorld . multiplyRange ( ecefRange ) ) ) ; } else { let rootTransform = RealityModelTileUtils . transformFromJson ( json . root . transform ) ; const range = RealityModelTileUtils . rangeFromBoundingVolume ( json . root . boundingVolume ) ! ; if ( undefined === rootTransform ) rootTransform = Transform . createIdentity ( ) ; const tileRange = rootTransform . multiplyRange ( range ) ; if ( rootTransform . matrix . isIdentity && range . center . magnitude ( ) < 1.0E5 ) { geoLocated = false ; worldRange . extendRange ( Range3d . fromJSON ( tileRange ) ) ; } else { const ecefCenter = tileRange . localXYZToWorld ( .5 , .5 , .5 ) ! ; const cartoCenter = Cartographic . fromEcef ( ecefCenter ) ; const ecefLocation = EcefLocation . createFromCartographicOrigin ( cartoCenter ! ) ; this . iModelDb . setEcefLocation ( ecefLocation ) ; const ecefToWorld = ecefLocation . getTransform ( ) . inverse ( ) ! ; worldRange . extendRange ( Range3d . fromJSON ( ecefToWorld . multiplyRange ( tileRange ) ) ) ; } } return { realityModel : { tilesetUrl : this . url , name : this . _name ? this . _name : this . url } , geoLocated } ; } /** Perform the import */ public async create ( ) { this . definitionModelId = DefinitionModel . insert ( this . iModelDb , IModelDb . rootSubjectId , "Definitions" ) ; this . physicalModelId = PhysicalModel . insert ( this . iModelDb , IModelDb . rootSubjectId , "Empty Model" ) ; let geoLocated = false ; const worldRange = new Range3d ( ) ; const realityModels = [ ] ; let json ; try { json = await fetch ( this . url , { method : "GET" , headers : { "Content-Type" : "application/json" } } ) ; json = json . json ( ) ; } catch ( error ) { process . stdout . write ( `Error occurred requesting data from: ${ this . url }Error: ${ error }\n` ) ; } if ( this . url . endsWith ( "_AppData.json" ) ) { const nameIndex = this . url . lastIndexOf ( "TileSets" ) ; const prefix = this . url . substr ( 0 , nameIndex ) ; let worldToEcef ; for ( const modelValue of Object . values ( json . models ) ) { const model = modelValue as any ; if ( model . tilesetUrl !== undefined && model . type === "spatial" ) { let modelUrl = prefix + model . tilesetUrl . replace ( /\/\//g , "/" ) ; modelUrl = modelUrl . replace ( / /g , "%20" ) ; const ecefRange = Range3d . fromJSON ( model . extents ) ; if ( ! worldToEcef ) { worldToEcef = RealityModelTileUtils . transformFromJson ( model . transform ) ! ; const ecefCenter = ecefRange . localXYZToWorld ( .5 , .5 , .5 ) ! ; const cartoCenter = Cartographic . fromEcef ( ecefCenter ) ; const ecefLocation = EcefLocation . createFromCartographicOrigin ( cartoCenter ! ) ; this . iModelDb . setEcefLocation ( ecefLocation ) ; geoLocated = true ; } worldRange . extendRange ( worldToEcef . inverse ( ) ! . multiplyRange ( ecefRange ) ) ; realityModels . push ( { tilesetUrl : modelUrl , name : this . _name ? this . _name : model . name } ) ; } } } else { const result = this . realityModelFromJson ( json , worldRange ) ; if ( result . realityModel ) { realityModels . push ( result . realityModel ) ; if ( result . geoLocated ) geoLocated = true ; realityModels . push ( ) ; } } this . insertSpatialView ( "Reality Model View" , worldRange , realityModels , geoLocated ) ; this . iModelDb . updateProjectExtents ( worldRange ) ; this . iModelDb . saveChanges ( ) ; } /** Insert a SpatialView configured to display the GeoJSON data that was converted/imported. */ protected insertSpatialView ( viewName , range , realityModels , geoLocated ) { const modelSelectorId = ModelSelector . insert ( this . iModelDb , this . definitionModelId , viewName , [ this . physicalModelId ] ) ; const categorySelectorId = CategorySelector . insert ( this . iModelDb , this . definitionModelId , viewName , [ ] ) ; const vf = new ViewFlags ( { backgroundMap : geoLocated , renderMode : RenderMode . SmoothShade , lighting : true } ) ; const displayStyleId = DisplayStyle3d . insert ( this . iModelDb , this . definitionModelId , viewName , { viewFlags : vf , contextRealityModels : realityModels } ) ; return OrthographicViewDefinition . insert ( this . iModelDb , this . definitionModelId , viewName , modelSelectorId , categorySelectorId , displayStyleId , range , StandardViewIndex . Iso ) ; } }