Camera FOV

619
3
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 Esteemed Contributor

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

3 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

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.