Camera FOV

1495
6
Jump to solution
12-11-2018 07:15 AM
AveryPenniston
New Contributor II

I've been working with a simple test app trying to manipulate a SceneView's camera.  I am able to reposition, pan and tilt the camera using the position, heading and tilt properties.   However I would also like to change the field of view of the camera's frustum.  I see there is an fov property on the camera, but it appears to do nothing.  I can assign values to this property and read them out again, but it seems to make no difference to the resulting view.

Is there a purpose to the fov property, or a specific use case that it is used for?

Thanks.

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Avery,

  Adding the camera fov property does change the scenes view with each change for me. Change the fov from 30 to 55 to 80 to 120 and see how it changes the scene.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Update the camera in a 3D view - 4.9</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }

    #buttonsDiv {
      position: absolute;
      top: 12px;
      right: 12px;
      padding: 12px;
      background-color: rgba(200, 200, 200, 0.5);
      border: 1px solid black;
    }

    #indicatorSpan {
      display: inline-block;
      vertical-align: middle;
      width: 30px;
      height: 30px;
      background-color: rgba(100, 100, 100, 0.8);
      border: 2px solid #ccc;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.9/esri/css/main.css">
  <script src="https://js.arcgis.com/4.9/"></script>

  <script>
    require([
      "esri/Map",
      "esri/views/SceneView"
    ], function(Map, SceneView) {

      // Create the Map
      var map = new Map({
        basemap: "hybrid",
        ground: "world-elevation",
      });

      // Create the SceneView
      var view = new SceneView({
        map: map,
        container: "viewDiv",
        camera: {
          position: [7.654, 45.919, 5183],
          tilt: 80,
          fov: 30
        }
      });

      // Register events to control
      var rotateAntiClockwiseSpan = document.getElementById(
        "rotateAntiClockwiseSpan");
      var rotateClockwiseSpan = document.getElementById(
        "rotateClockwiseSpan");
      var indicatorSpan = document.getElementById("indicatorSpan");
      rotateClockwiseSpan.addEventListener("click", function() {
        rotateView(-1);
      });
      rotateAntiClockwiseSpan.addEventListener("click", function() {
        rotateView(1);
      });
      indicatorSpan.addEventListener("click", tiltView);

      // Watch the change on view.camera
      view.watch("camera", updateIndicator);

      // Create the event's callback functions
      function rotateView(direction) {
        var heading = view.camera.heading;

        // Set the heading of the view to the closest multiple of 90 degrees,
        // depending on the direction of rotation
        if (direction > 0) {
          heading = Math.floor((heading + 1e-3) / 90) * 90 + 90;
        } else {
          heading = Math.ceil((heading - 1e-3) / 90) * 90 - 90;
        }

        view.goTo({
          heading: heading
        });
      }

      function tiltView() {
        // Get the camera tilt and add a small number for numerical inaccuracies
        var tilt = view.camera.tilt + 1e-3;

        // Switch between 3 levels of tilt
        if (tilt >= 80) {
          tilt = 0;
        } else if (tilt >= 40) {
          tilt = 80;
        } else {
          tilt = 40;
        }

        view.goTo({
          tilt: tilt
        });
      }

      function updateIndicator(camera) {
        var tilt = camera.tilt;
        var heading = camera.heading;

        // Update the indicator to reflect the current tilt/heading using
        // css transforms.
        var transform = "rotateX(" + 0.8 * tilt +
          "deg) rotateY(0) rotateZ(" + -heading +
          "deg)";

        indicatorSpan.style["transform"] = transform;
        indicatorSpan.style["-webkit-transform"] = transform; // Solution for Safari
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
  <div id="buttonsDiv">
    <button id="rotateAntiClockwiseSpan" title="Rotate 90°"></button>
    <button id="indicatorSpan"></button>
    <button id="rotateClockwiseSpan" title="Rotate 90°"></button>
  </div>
</body>
</html>

View solution in original post

6 Replies
RobertScheitlin__GISP
MVP Emeritus

Avery,

  Adding the camera fov property does change the scenes view with each change for me. Change the fov from 30 to 55 to 80 to 120 and see how it changes the scene.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Update the camera in a 3D view - 4.9</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }

    #buttonsDiv {
      position: absolute;
      top: 12px;
      right: 12px;
      padding: 12px;
      background-color: rgba(200, 200, 200, 0.5);
      border: 1px solid black;
    }

    #indicatorSpan {
      display: inline-block;
      vertical-align: middle;
      width: 30px;
      height: 30px;
      background-color: rgba(100, 100, 100, 0.8);
      border: 2px solid #ccc;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.9/esri/css/main.css">
  <script src="https://js.arcgis.com/4.9/"></script>

  <script>
    require([
      "esri/Map",
      "esri/views/SceneView"
    ], function(Map, SceneView) {

      // Create the Map
      var map = new Map({
        basemap: "hybrid",
        ground: "world-elevation",
      });

      // Create the SceneView
      var view = new SceneView({
        map: map,
        container: "viewDiv",
        camera: {
          position: [7.654, 45.919, 5183],
          tilt: 80,
          fov: 30
        }
      });

      // Register events to control
      var rotateAntiClockwiseSpan = document.getElementById(
        "rotateAntiClockwiseSpan");
      var rotateClockwiseSpan = document.getElementById(
        "rotateClockwiseSpan");
      var indicatorSpan = document.getElementById("indicatorSpan");
      rotateClockwiseSpan.addEventListener("click", function() {
        rotateView(-1);
      });
      rotateAntiClockwiseSpan.addEventListener("click", function() {
        rotateView(1);
      });
      indicatorSpan.addEventListener("click", tiltView);

      // Watch the change on view.camera
      view.watch("camera", updateIndicator);

      // Create the event's callback functions
      function rotateView(direction) {
        var heading = view.camera.heading;

        // Set the heading of the view to the closest multiple of 90 degrees,
        // depending on the direction of rotation
        if (direction > 0) {
          heading = Math.floor((heading + 1e-3) / 90) * 90 + 90;
        } else {
          heading = Math.ceil((heading - 1e-3) / 90) * 90 - 90;
        }

        view.goTo({
          heading: heading
        });
      }

      function tiltView() {
        // Get the camera tilt and add a small number for numerical inaccuracies
        var tilt = view.camera.tilt + 1e-3;

        // Switch between 3 levels of tilt
        if (tilt >= 80) {
          tilt = 0;
        } else if (tilt >= 40) {
          tilt = 80;
        } else {
          tilt = 40;
        }

        view.goTo({
          tilt: tilt
        });
      }

      function updateIndicator(camera) {
        var tilt = camera.tilt;
        var heading = camera.heading;

        // Update the indicator to reflect the current tilt/heading using
        // css transforms.
        var transform = "rotateX(" + 0.8 * tilt +
          "deg) rotateY(0) rotateZ(" + -heading +
          "deg)";

        indicatorSpan.style["transform"] = transform;
        indicatorSpan.style["-webkit-transform"] = transform; // Solution for Safari
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
  <div id="buttonsDiv">
    <button id="rotateAntiClockwiseSpan" title="Rotate 90°"></button>
    <button id="indicatorSpan"></button>
    <button id="rotateClockwiseSpan" title="Rotate 90°"></button>
  </div>
</body>
</html>
AveryPenniston
New Contributor II

Thanks for the example Robert. 

I should have clarified that I wanted to do this at runtime.  I can change the heading and tilt values on a camera and then call the goTo() function on the scene view in order to reposition the view at runtime.   However that wouldn't work when changing the fov on the camera.  

I did use your example to help me figure out that I need to recreate the sceneview with the new camera in order to change the fov on it, which does work.  So for anyone else that wants to modify the fov at runtime, you will need to recreate the sceneview using the camera with a new fov value, rather than just being able to use the goTo function with that new camera.

JohnGrayson
Esri Regular Contributor

I believe the goTo(...) method can take a Viewpoint for the 'target' parameter, and the Viewpoint has a 'camera' parameter, so I wonder if you could manipulate the fov in this manner as part of calling goTo(...)?  Another way to directly manipulate the Camera properties it to clone the camera, change the properties, then re-assign it to the view.

jauhari_mani
Occasional Contributor

Hi @RobertScheitlin__GISP ,

Is there a way to draw a frustum similar to the image attached, which depicts the coverage area of an image on Earth from the angle of the camera?

jauhari_mani_0-1681998512222.png

 

 

0 Kudos
Mark_Wilson
New Contributor II

Is there a way to change the Horizontal FoV for a scene view in the script please that you can share? For landscape and visual impact assessment we have to produce visualisations to set HFoVs often 90 and 53.5 degree. Many thanks in anticipation

0 Kudos
Mark_Wilson
New Contributor II

Hi I am interested in being able to adjust the fov of the camera when moving between viewpoints for a landscape and visual assessment project which requires specific visualisations with the HFoVs often 90 and 53.5. I have tried with the following script for Viewpoint 2. I can adjust tilt and position but not fov. I have read through the above thread but not sure how to implement the changes I need to the following script. Many thanks in advance. 

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <!--
     ArcGIS Maps SDK for JavaScript, https://js.arcgis.com
     For more information about the scene-goto sample,
     read the original sample description at developers.arcgis.com.
     https://developers.arcgis.com/javascript/latest/sample-code/scene-goto/
     -->
<title>SceneView - goTo() | Sample | ArcGIS Maps SDK for JavaScript 4.29</title>

    <link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.29/"></script>

    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }

      #optionsDiv {
        position: absolute;
        bottom: 17px;
        width: 100%;
        padding: 20px 0;
        z-index: 1;
        text-align: center;
      }

      button {
        background: white;
        padding: 7px;
        border: 1px solid #005e95;
        font-size: 0.9em;
        margin: 5px;
        color: #005e95;
      }

      button:hover {
        background: #005e95;
        color: white;
        cursor: pointer;
      }
    </style>

    <script>
      require([ "esri/config","esri/Map", "esri/views/SceneView","esri/WebScene","esri/widgets/Legend"
              ], function(esriConfig, Map, SceneView, WebScene,Legend)  {
        
        esriConfig.apiKey = "***APIKey****";
        
        /**const map = new Map({
          basemap: "dark-gray-vector"
        });
        **/

        /*********************************************************************
         * Create a new WebScene referencing a WebScene ID from ArcGIS Online
         * or an on-premise portal.
         *********************************************************************/
        const scene = new WebScene({
          portalItem: {
            // autocasts as new PortalItem()
            id: "***ID Here***"
          }
        });

        /*********************************************************************
         * Reference the WebScene in a SceneView instance.
         *********************************************************************/
        
        const view = new SceneView({
          map: scene,
          container: "viewDiv"
       
          });

        view.ui
        

        /*****************************************************************
         *
         * Add event listeners to go to a target point using animation options
         *
         *****************************************************************/

        // The target point is a new camera obtained by shifting the current camera to the new VP not resolved this issue as fov not updating

        function shiftCamera(position) {
          const camera = view.camera.clone();
          camera.position.tilt.fov;
          return camera;
        }

        function catchAbortError(error) {
          if (error.name != "AbortError") {
            console.error(error);
          }
        }


        // Define your own function for the easing option
        function customEasing(t) {
          return 1 - Math.abs(Math.sin(-1.7 + t * 4.5 * Math.PI)) * Math.pow(0.5, t * 10);
        }

        document.getElementById("defaultVP1").addEventListener("click", () => {
          view
            
            .goTo(
              {
                position: {
                 x: -3.484,
                  y: 51.563,
                  z: 130,
                
                  spatialReference: {
                    wkid: 4326
                  }
                },
                heading: 260,
                tilt: 90,
                fov: 90
            
              },
              {
                speedFactor: 0.6,
                easing: "linear"
              }
            )
            .catch(catchAbortError);
        });

        document.getElementById("gotoVP2_90deg").addEventListener("click", () => {
          view
            .goTo(
              {
                position: {
                  x: -3.489667,
                  y: 51.557520,
                  z: 88.5,
                  spatialReference: {
                    wkid: 4326
                  }
                },
                heading: 280,
                tilt: 92,
                fov:90
              },
              {
                speedFactor: 0.6,
                easing: "linear"
              }
            )
            .catch(catchAbortError);
        });

        document.getElementById("gotoVP2_53.5deg").addEventListener("click", () => {
          view
            .goTo(
              {
                position: {
                  x: -3.489667,
                  y: 51.557520,
                  z: 88.5,
                  spatialReference: {
                    wkid: 4326
                  }
                },
                heading: 280,
                tilt: 92,
                fov: 53.5
              },
              {
                speedFactor: 0.6,
                easing: "linear"
              }
            )
            .catch(catchAbortError);
        });
        
        });
    </script>
  </head>

  <body>
    <div id="optionsDiv">
      <button id="defaultVP1">Default to VP1</button>
      <button id="gotoVP2_90deg">Go to VP2 (90deg)</button>
      <button id="gotoVP2_53.5deg">Go to VP2 (53.5deg)</button>
      

    </div>
    <div id="viewDiv"></div>
  </body>
</html>

 

 

0 Kudos