"use strict"; // Copyright 2012 United States Government, as represented by the Secretary of Defense, Under // Secretary of Defense (Personnel & Readiness). // // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software distributed under the License // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express // or implied. See the License for the specific language governing permissions and limitations under // the License. /// vwf/view/document extends a view interface up to the browser document. When vwf/view/document /// is active, scripts on the main page may make (reflected) kernel calls: /// /// window.vwf_view.createNode( nodeID, childID, childExtendsID, childImplementsIDs, /// childSource, childType, childName, function( childID ) { /// ... /// } ); /// /// And receive view calls: /// /// window.vwf_view.createdNode = function( nodeID, childID, childExtendsID, childImplementsIDs, /// childSource, childType, childName, callback /- ( ready ) -/ ) { /// ... /// } /// /// @module vwf/view/cesium /// @requires vwf/view define( [ "module", "vwf/view", "vwf/utility", "vwf/model/cesium/Cesium", "jquery" ], function( module, view, utility, Cesium, jQuery ) { return view.load( module, { // == Module Definition ==================================================================== initialize: function( options ) { if ( !this.state ) { this.state = {}; } if ( !this.state.scenes ) { this.state.scenes = {}; } if ( !this.state.nodes ) { this.state.nodes = {}; } if ( options === undefined ) { options = {}; } // html creation options this.parentDiv = options.parentDiv || 'body'; this.parentClass = options.parentClass || 'cesium-main-div'; this.containerClass = options.containerClass || 'fullSize'; this.container = options.container || { "create": true, "divName": "cesiumContainer" } ; // Context and WebGL creation properties corresponding to options passed to Scene. this.canvasOptions = options.canvasOptions; this.invertMouse = options.invertMouse || {}; this.cesiumType = options.cesium || 'widget'; // 'widget', 'viewer', manual - anything else // CesiumWidget options, will accept all defaults this.cesiumTypeOptions = options.widget || { //"clock": false, //"imageryProvider": 'OpenStreetMapImageryProvider', //"terrainProvider": 'CesiumTerrainProvider', //"skyBox": {}, "sceneMode": Cesium.SceneMode.SCENE3D, "scene3DOnly": false, "orderIndependentTranslucency": true, //"mapProjection": GeographicProjection || WebMercatorProjection "useDefaultRenderLoop": true, //"targetFrameRate": '?', "showRenderLoopErrors": true, //"contextOptions": {}, // Canvas options //"creditContainer": undefined, // DOM element or ID of where the credits go }; this.height = 600; this.width = 800; if ( window ) { if ( window.innerHeight ) this.height = window.innerHeight - 20; if ( window.innerWidth ) this.width = window.innerWidth - 20; this.window = window; } this.state.clientControl = { event: undefined, // probably not needed but this will contain the controller: "", // this is the moniker of the locked: false }; }, createdNode: function( nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childIndex, childName, callback /* ( ready ) */ ) { if ( childExtendsID === undefined ) return; //this.logger.infox( "createdNode", nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childName ); var createNode = function() { return { parentID: nodeID, ID: childID, extendsID: childExtendsID, implementsIDs: childImplementsIDs, source: childSource, type: childType, name: childName, loadComplete: callback }; }; var kernel = this.kernel; var protos = getPrototypes.call( this, childExtendsID ) var node = undefined; if ( isCesiumDefinition.call( this, protos ) ) { if ( this.container.create ) { //var cesiumCont = "
"; var cesiumCont = "
"; if ( this.parentDiv == 'body' ) { jQuery( this.parentDiv ).append( cesiumCont ); } else { var outDiv; if ( this.parentClass !== undefined ) { outDiv = "
"+cesiumCont+"
"; } else { outDiv = "
"+cesiumCont+"
" } jQuery( 'body' ).append( outDiv ); } } if ( this.state.scenes[ childID ] === undefined ) { this.state.scenes[ childID ] = node = createNode(); } else { node = this.state.scenes[ childID ]; } var view = this; var forceResizeDelay = 60; var scene, canvas; // options for oneToOne below //var cesiumOptions = { "contextOptions": { "alpha": true }, }; var options = this.cesiumTypeOptions; switch ( this.cesiumType ) { case 'widget': options.clock && this.state.createClock( options ); options.imageryProvider && this.state.createImageryProvider( options ); options.terrainProvider && this.state.createTerrainProvider( options ); options.skyBox && this.state.createSkyBox( options ); options.sceneMode && this.state.setSceneMode( options ); options.mapProjection && this.state.createMapProjection( options ); node.cesiumWidget = new Cesium.CesiumWidget( this.container.divName, options ); node.scene = scene = node.cesiumWidget.scene; node.globe = scene.globe; break; case 'viewer': node.cesiumViewer = new Cesium.Viewer( this.container.divName, this.cesiumTypeOptions ); node.cesiumWidget = node.cesiumViewer.cesiumWidget; node.scene = scene = node.cesiumViewer.scene; node.globe = scene.globe; break; default: // the manual creation, has an error with the // camera syncronization canvas = document.createElement( 'canvas' ); canvas.className = 'fullSize'; document.getElementById( this.container.divName ).appendChild( canvas ); canvas.setAttribute( 'height', this.height ); canvas.setAttribute( 'width', this.width ); node.scene = scene = new Cesium.Scene( canvas, cesiumOptions.contextOptions ); var bing = new Cesium.BingMapsImageryProvider({ url : 'http://dev.virtualearth.net', mapStyle : Cesium.BingMapsStyle.AERIAL, // Some versions of Safari support WebGL, but don't correctly implement // cross-origin image loading, so we need to load Bing imagery using a proxy. proxy : Cesium.FeatureDetection.supportsCrossOriginImagery() ? undefined : new Cesium.DefaultProxy('/proxy/') }); var primitives = scene.getPrimitives(); var ellipsoid = Cesium.Ellipsoid.WGS84; node.globe = new Cesium.Globe( ellipsoid ); node.globe.getImageryLayers().addImageryProvider( bing ); primitives.setGlobe( node.globe ); node.transitioner = new Cesium.SceneTransitioner( scene, ellipsoid ); break; } node.imageryProvider = 'bingAerial'; node.canvas = scene._canvas; scene.vwfID = childID; initializeMouseEvents.call( this, scene, node ); var camera = scene._camera; ( function tick() { if ( view.state.cameraInfo ) { if ( view.state.clientControl.controller == view.kernel.moniker() ) { var diffs = view.state.cameraInfo.diff( camera ); if ( diffs !== undefined ){ broadcastCameraViewData.call( view, diffs ); } } } else { view.state.cameraInfo = { "initialized": false, "direction": undefined, "position": undefined, "up": undefined, "right": undefined, //"earthDistance": 0, "equals": function( v1, v2 ) { return ( ( Math.round( v1.x ) == Math.round( v2.x ) ) && ( Math.round( v1.y ) == Math.round( v2.y ) ) && ( Math.round( v1.z ) == Math.round( v2.z ) ) ); }, "diff": function( cam ) { var retObj = undefined; if ( this.initialized ) { if ( !Cesium.Cartesian3.equals( this.direction, cam.direction ) ){ retObj = { "direction": [ cam.direction.x, cam.direction.y, cam.direction.z ] }; } if ( !this.equals( this.position, cam.position ) ) { if ( retObj === undefined ) { retObj = { "position": [ cam.position.x, cam.position.y, cam.position.z ] }; } else { retObj.position = [ cam.position.x, cam.position.y, cam.position.z ]; } //var dist = Math.round( this.calcDistanceToOrigin( cam ) ); //var tolerance = 2; //if ( dist <= this.earthDistance - tolerance || dist >= this.earthDistance + tolerance ) { // is there a way to fire an event from here? // console.info( "change in the distance to earth:" + this.earthDistance ); //} //this.earthDistance = dist; } if ( !Cesium.Cartesian3.equals( this.up, cam.up ) ) { if ( retObj === undefined ) { retObj = { "up": [ cam.up.x, cam.up.y, cam.up.z ] }; } else { retObj.up = [ cam.up.x, cam.up.y, cam.up.z ]; } } if ( !Cesium.Cartesian3.equals( this.right, cam.right ) ) { if ( retObj === undefined ) { retObj = { "right": [ cam.right.x, cam.right.y, cam.right.z ] }; } else { retObj.right = [ cam.right.x, cam.right.y, cam.right.z ]; } } } return retObj; }, // "calcDistanceToOrigin": function( obj ) { // if ( obj.position ){ // var p = obj.position; // return Math.sqrt( ( p.x * p.x ) + ( p.y * p.y ) + ( p.z * p.z ) ); // } // return undefined; // }, "getCurrent": function( cam ) { this.direction = camera.direction.clone(); this.position = camera.position.clone(); this.up = camera.up.clone(); this.right = camera.right.clone(); }, "isInitialized": function() { this.initialized = ( ( this.direction != undefined ) && ( this.position != undefined ) && ( this.up != undefined ) && ( this.right != undefined ) ); } }; } if ( forceResizeDelay ) { forceResizeDelay--; if ( forceResizeDelay == 0 ) { node.cesiumWidget.resize(); forceResizeDelay = undefined; view.state.cameraInfo.initialized = true; } } scene.initializeFrame(); scene.render(); Cesium.requestAnimationFrame( tick ); if ( forceResizeDelay === undefined ) { view.state.cameraInfo.getCurrent( camera ); } }()); if ( !this.useCesiumWidget ) { var onResize = function () { var width = node.canvas.clientWidth; var height = node.canvas.clientHeight; if ( node.canvas.width === width && node.canvas.height === height ) { return; } node.canvas.width = width; node.canvas.height = height; camera.frustum.aspectRatio = width / height; }; window.addEventListener( 'resize', onResize, false ); onResize(); //document.oncontextmenu = function() { return false; }; } } }, //deletedNode: function (nodeID) { //}, //createdProperty: function (nodeID, propertyName, propertyValue) { //}, //initializedProperty: function (nodeID, propertyName, propertyValue) { //}, satProperty: function( nodeID, propertyName, propertyValue ) { switch ( propertyName ) { } }, //gotProperty: function( nodeID, propertyName, propertyValue ) { //} } ); function getPrototypes( extendsID ) { var prototypes = []; var id = extendsID; while ( id !== undefined ) { prototypes.push( id ); id = this.kernel.prototype( id ); } return prototypes; } function isCesiumDefinition( prototypes ) { var foundCesium = false; if ( prototypes ) { var len = prototypes.length; for ( var i = 0; i < len && !foundCesium; i++ ) { foundCesium = ( prototypes[i] == "http://vwf.example.com/cesium.vwf" ); } } return foundCesium; } function isSunDefinition( prototypes ) { var foundCesium = false; if ( prototypes ) { var len = prototypes.length; for ( var i = 0; i < len && !foundCesium; i++ ) { foundCesium = ( prototypes[i] == "http://vwf.example.com/cesium/sun.vwf" ); } } return foundCesium; } function isAtmosphereDefinition( prototypes ) { var foundCesium = false; if ( prototypes ) { var len = prototypes.length; for ( var i = 0; i < len && !foundCesium; i++ ) { foundCesium = ( prototypes[i] == "http://vwf.example.com/cesium/atmosphere.vwf" ); } } return foundCesium; } function isSkyBoxDefinition( prototypes ) { var foundCesium = false; if ( prototypes ) { var len = prototypes.length; for ( var i = 0; i < len && !foundCesium; i++ ) { foundCesium = ( prototypes[i] == "http://vwf.example.com/cesium/skybox.vwf" ); } } return foundCesium; } function broadcastCameraViewData(cameraData) { var nodeID, scene; if ( this.kernel.find( "", "//" ).length > 0 ) { nodeID = this.kernel.find( "", "//" )[ 0 ]; this.kernel.setProperty( nodeID, "cameraViewData", cameraData ); } } function initializeMouseEvents( scene, node ) { this.state.mouse = { "enabled": true, "handler": undefined, "leftDown": false, "leftDownID": undefined, "middleDown": false , "middleDownID": undefined, "rightDown": false, "rightDownID": undefined, "pinching": false, "zooming": false, "scene": scene, "lastPosition": [ -1, -1 ], "initialDown": undefined, "controlState": undefined, "enable": function( value ) { if ( value != this.enabled ) { if ( value ) { this.restoreControlState(); } else { this.captureControlState(); this.setControlState( false ); } this.enabled = value; } }, "active": function() { return this.leftDown || this.middleDown || this.rightDown || this.zooming || this.pinching; }, "buttonDown": function() { if ( this.leftDown ) { return "left"; } else if ( this.rightDown ) { return "right"; } else if ( this.middleDown ) { return "middle"; } return undefined; }, "setControlState": function( value ) { if ( this.handler ) { this.handler.enableLook = value; this.handler.enableRotate = value; this.handler.enableTilt = value; this.handler.enableTranslate = value; this.handler.enableZoom = value; } this.controlState = undefined; }, "captureControlState": function() { this.controlState = { "look": this.handler.enableLook, "rotate": this.handler.enableRotate, "tilt": this.handler.enableTilt, "translate": this.handler.enableTranslate, "zoom": this.handler.enableZoom }; }, "restoreControlState": function() { if ( this.controlState ) { this.handler.enableLook = this.controlState.look; this.handler.enableRotate = this.controlState.rotate; this.handler.enableTilt = this.controlState.tilt; this.handler.enableTranslate = this.controlState.translate; this.handler.enableZoom = this.controlState.zoom; } this.controlState = undefined; } }; var overID = undefined; var downID = undefined; var lastOverID = undefined; var sceneCanvas = scene._canvas; var rootID = this.kernel.find( "", "/" )[0]; this.state.mouse.handler = new Cesium.ScreenSpaceEventHandler( sceneCanvas ); if ( this.state.mouse.handler ) { var mouse = this.state.mouse.handler; var self = this; var getMousePosition = function( pos ) { var posRet = { "x": pos.x, "y": pos.y }; if ( self.invertMouse.x !== undefined ) { posRet.x = sceneCanvas.width - posRet.x; } if ( self.invertMouse.y !== undefined ) { posRet.y = sceneCanvas.height - posRet.y; } return posRet; } var pick = function( button, clickCount, event, position ) { var pos = getMousePosition( position ); var height = scene._canvas.height; var width = scene._canvas.width; var eventObj = self.state.mouse.scene.pick( pos ); var ellipsoid = scene._globe._ellipsoid; var globePoint = scene._camera.pickEllipsoid( pos, ellipsoid ); var camPos = scene._camera.position; var eventID; if ( eventObj ) { eventID = eventObj.vwfID; } else if ( globePoint !== undefined ) { eventID = self.kernel.find( rootID, "earth" )[0]; } else { eventID = rootID; } var eData = { "eventData": [ { "button": button, "clicks": clickCount, "buttons": { "left": self.state.mouse.leftDown, "middle": self.state.mouse.middleDown, "right": self.state.mouse.rightDown }, "modifiers": { "alt": false, "ctrl": false, "shift": false, "meta": false }, "position": [ pos.x / width, pos.y / height ], "screenPosition": [ pos.x, pos.y ] } ], "eventNodeData": { "": [ { "distance": undefined, "origin": [ camPos.x, camPos.y, camPos.z ], "id": eventID, "globalPosition": globePoint ? [ globePoint.x, globePoint.y, globePoint.z ] : undefined, "globalNormal": undefined, "globalSource": [ camPos.x, camPos.y, camPos.z ], } ] }, }; if ( event == "down" ) { switch( button ) { case "left": self.state.mouse.leftDownID = eventID; break; case "middle": self.state.mouse.middleDownID = eventID; break; case "right": self.state.mouse.rightDownID = eventID; break; } downID = eventID; } else if ( ( event == "up" ) || ( event == "drag" ) ) { switch( button ) { case "left": if ( self.state.mouse.leftDownID !== undefined ) { eventID = self.state.mouse.leftDownID; } break; case "middle": if ( self.state.mouse.middleDownID !== undefined ) { eventID = self.state.mouse.middleDownID; } break; case "right": if ( self.state.mouse.rightDownID !== undefined ) { eventID = self.state.mouse.rightDownID; } break; } } else if ( event == "move" ) { overID = eventID; } if ( eventID && eventID != rootID ) { var id = eventID; while ( id && id != rootID ) { eData.eventNodeData[ id ] = [ { "distance": undefined, "origin": scene._camera.position, "globalPosition": globePoint ? [ globePoint.x, globePoint.y, globePoint.z ] : undefined, "globalNormal": undefined, "globalSource": scene._camera.position, } ]; //id = undefined; if ( self.state.nodes[ id ] ) { id = self.state.nodes[ id ].parentID; } else { id = undefined; } } } return eData; } // left click mouse.setInputAction( function( movement ) { var eData = pick( "left", 1, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.LEFT_CLICK ); // left double click mouse.setInputAction( function( movement ) { var eData = pick( "left", 2, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerDoubleClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK ); // left up mouse.setInputAction( function( movement ) { self.state.mouse.leftDown = false; var eData = pick( "left", 0, "up", movement.position ); if ( downID !== undefined ) { self.kernel.dispatchEvent( downID, "pointerUp", eData.eventData, eData.eventNodeData ); } self.kernel.setProperty( rootID, "clientControl", { event: 'left', controller: self.kernel.moniker(), locked: false } ); self.state.mouse.leftDownID = undefined; }, Cesium.ScreenSpaceEventType.LEFT_UP ); // left down mouse.setInputAction( function( movement ) { self.kernel.setProperty( rootID, "clientControl", { event: 'left', controller: self.kernel.moniker(), locked: true } ); self.state.mouse.leftDown = true; var eData = pick( "left", 0, "down", movement.position ); self.kernel.dispatchEvent( downID, "pointerDown", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.LEFT_DOWN ); // mouse move mouse.setInputAction( function( movement ) { var bd = self.state.mouse.buttonDown(); if ( bd ) { var eData = pick( bd, 0, "drag", movement.endPosition ); self.kernel.dispatchEvent( downID, "pointerMove", eData.eventData, eData.eventNodeData ); } else { var eData = pick( "", 0, "move", movement.endPosition ); if ( lastOverID === undefined && overID !== undefined ) { self.kernel.dispatchEvent( overID, "pointerEnter", eData.eventData, eData.eventNodeData ); lastOverID = overID; } else if ( overID ) { if ( overID !== lastOverID ) { self.kernel.dispatchEvent( lastOverID, "pointerLeave", eData.eventData, eData.eventNodeData ); self.kernel.dispatchEvent( overID, "pointerEnter", eData.eventData, eData.eventNodeData ); lastOverID = overID; } else { self.kernel.dispatchEvent( overID, "pointerOver", eData.eventData, eData.eventNodeData ); } } } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE ); // middle click mouse.setInputAction( function( movement ) { var eData = pick( "middle", 1, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.MIDDLE_CLICK ); // middle double click mouse.setInputAction( function( movement ) { var eData = pick( "middle", 2, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerDoubleClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.MIDDLE_DOUBLE_CLICK ); // middle up mouse.setInputAction( function( movement ) { self.state.mouse.middleDown = false; var eData = pick( "middle", 1, "up", movement.position ); self.kernel.dispatchEvent( downID, "pointerUp", eData.eventData, eData.eventNodeData ); self.state.mouse.middleDownID = undefined; self.kernel.setProperty( rootID, "clientControl", { event: 'middle', controller: self.kernel.moniker(), locked: false } ); }, Cesium.ScreenSpaceEventType.MIDDLE_UP ); // middle down mouse.setInputAction( function( movement ) { self.kernel.setProperty( rootID, "clientControl", { event: 'middle', controller: self.kernel.moniker(), locked: true } ); self.state.mouse.middleDown = true; var eData = pick( "middle", 0, "down", movement.position ); self.kernel.dispatchEvent( downID, "pointerDown", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.MIDDLE_DOWN ); // right click mouse.setInputAction( function( movement ) { var eData = pick( "right", 1, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.RIGHT_CLICK ); // right double click mouse.setInputAction( function( movement ) { var eData = pick( "right", 2, "click", movement.position ); self.kernel.dispatchEvent( downID, "pointerDoubleClick", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.RIGHT_DOUBLE_CLICK ); // right up mouse.setInputAction( function( movement ) { self.state.mouse.rightDown = false; var eData = pick( "right", 0, "up", movement.position ); self.kernel.dispatchEvent( downID, "pointerUp", eData.eventData, eData.eventNodeData ); self.state.mouse.rightDownID = undefined; self.kernel.setProperty( rootID, "clientControl", { event: 'right', controller: self.kernel.moniker(), locked: false } ); }, Cesium.ScreenSpaceEventType.RIGHT_UP ); // right down mouse.setInputAction( function( movement ) { self.kernel.setProperty( rootID, "clientControl", { event: 'right', controller: self.kernel.moniker(), locked: true } ); self.state.mouse.rightDown = true; var eData = pick( "right", 0, "down", movement.position ); self.kernel.dispatchEvent( downID, "pointerDown", eData.eventData, eData.eventNodeData ); }, Cesium.ScreenSpaceEventType.RIGHT_DOWN ); // pinch start mouse.setInputAction( function( movement ) { self.kernel.setProperty( rootID, "clientControl", { event: 'pinch', controller: self.kernel.moniker(), locked: true } ); self.state.mouse.pinching = true; }, Cesium.ScreenSpaceEventType.PINCH_START ); // pinch move mouse.setInputAction( function( movement ) { }, Cesium.ScreenSpaceEventType.PINCH_MOVE ); // pinch end mouse.setInputAction( function( movement ) { self.state.mouse.pinching = false; self.kernel.setProperty( rootID, "clientControl", { event: 'pinch', controller: self.kernel.moniker(), locked: false } ); }, Cesium.ScreenSpaceEventType.PINCH_END ); // wheel mouse.setInputAction( function( movement ) { self.kernel.setProperty( rootID, "clientControl", { event: 'wheel', controller: self.kernel.moniker(), locked: false } ); }, Cesium.ScreenSpaceEventType.WHEEL ); } } } );