Switch between 2D and 3D - ArcGIS JS API 4.15

1902
1
Jump to solution
06-17-2020 01:22 PM
GeorgeAbraham
New Contributor III

Hi Robert Scheitlin, GISP‌ / Experts,

I am trying to switch from 2D to 3D view and vice versa (followed the link Switch view from 2D to 3D | ArcGIS API for JavaScript 4.15 ) and facing some issues.

The layers are mainly oil pipelines, well pads, facilities, etc.

Each of the Feature Layer are added manually via FeatureService/0. Since the renderer is different for 2D and 3D, I need to be able to change the renderer to and re-render the map.

Snippet of code below:

var map = new Map({
        basemap: "topo-vector"
      });

      var switchButton = document.getElementById("switch-btn");

      var appConfig = {
        mapView: null,
        sceneView: null,
        activeView: null,
        container: "viewDiv",
        pipelineRenderer: null,
        wellpadRenderer: null,
        facilityRenderer: null
      };

      var initialViewParams = {
        zoom: 13,
        center: [-90.02620.745],
        container: appConfig.container,
        map: map
      };

      // create 2D view and and set active
      appConfig.mapView = createView(initialViewParams"2d");
      appConfig.activeView = appConfig.mapView;

      // create 3D view, won't initialize until container is set
      initialViewParams.container = null;
      appConfig.sceneView = createView(initialViewParams"3d");

      var view = appConfig.activeView;

      // Convenience function for creating a 2D or 3D view
      function createView(paramstype) {
        var view;
        var is2D = type === "2d";
        if (is2D) {
          view = new MapView(params);
          return view;
        } else {
          params.camera = {
            position: {
              x: -90.02,
              y: 20.745,
              z: 2000 // meters
            },
            tilt: 75
          };
          view = new SceneView(params);
        }
        return view;
      }
........
........
//Set inital Renderers
      appConfig.pipelineRenderer = pipelineRenderer;
      appConfig.wellpadRenderer = wellpadRenderer;
      appConfig.facilityRenderer = facilityRenderer;

      // Switch the view between 2D and 3D each time the button is clicked
      switchButton.addEventListener("click"function () {
        switchView();
      });

      // Switches the view from 2D to 3D and vice versa
      function switchView() {
        var is3D = appConfig.activeView.type === "3d";
        // var activeViewpoint = appConfig.activeView.viewpoint.clone();

        // Remove the reference to the container for the previous view
        appConfig.activeView.container = null;

        if (is3D) {
          // if the input view is a SceneView, set the viewpoint on the
          // mapView instance. Set the container on the mapView and flag
          // it as the active view
          // appConfig.mapView.viewpoint = activeViewpoint;
          // appConfig.mapView.container = appConfig.container;
          appConfig.activeView = appConfig.mapView;
          switchButton.value = "3D";
          appConfig.pipelineRenderer = pipelineRenderer;
          appConfig.wellpadRenderer = wellpadRenderer;
          appConfig.facilityRenderer = facilityRenderer;
        } else {
          // appConfig.sceneView.viewpoint = activeViewpoint;
          appConfig.sceneView.container = appConfig.container;
          appConfig.activeView = appConfig.sceneView;
          switchButton.value = "2D";

          // var pipeLineLayer = map.findLayerById("idPipelineLayer");
          // var facilityLayer = map.findLayerById("idFacilityLayer");
          // var wellpadLayer = map.findLayerById("idWellpadLayer");

          // pipeLineLayer.set(pipelineRenderer3D);
          // pipeLineLayer.refresh();

          // facilityLayer.set(facilityRenderer3D);
          // facilityLayer.refresh();

          // wellpadLayer.set(wellpadRenderer3D);
          // wellpadLayer.refresh();

          appConfig.pipelineRenderer = pipelineRenderer3D;
          appConfig.wellpadRenderer = wellpadRenderer3D;
          appConfig.facilityRenderer = facilityRenderer3D;
        }
      }

The issue is right when it calls the 

appConfig.activeView.container = null;

it starts throwing errors in the console as below:

and also below error on complete execution

And my 3D renderers don't come up.

NOTE:

  • Standalone my 3D layer renders perfectly fine as for my 2D, I am just trying to switch between them and vice versa.
  • Unfortunately I do not have a publicly available service for the layers to replicate the issue

Thanks in advance.

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

George,

   In this sample I do not have any issue with that.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>Switch view from 2D to 3D - 4.15</title>

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

      #infoDiv {
        position: absolute;
        top: 15px;
        left: 60px;
      }

      #infoDiv input {
        border: none;
        box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 2px;
      }
    </style>

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

    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/views/SceneView",
        "esri/layers/FeatureLayer"
      ], function(Map, MapView, SceneView, FeatureLayer) {
        var switchButton = document.getElementById("switch-btn");

        var appConfig = {
          mapView: null,
          sceneView: null,
          activeView: null,
          container: "viewDiv" // use same container for views
        };

        var initialViewParams = {
          zoom: 12,
          center: [-122.43759993450347, 37.772798684981126],
          container: appConfig.container
        };
        var featureLayer = new FeatureLayer({
          url:
            "https://services.arcgis.com/Zs2aNLFN00jrS4gG/arcgis/rest/services/PedInjuryCounts_2013/FeatureServer/0"
        });
        
        // Create objectSymbol and add to renderer
        var objectSymbol = {
          type: "point-3d", // autocasts as new PointSymbol3D()
          symbolLayers: [
            {
              type: "object", // autocasts as new ObjectSymbol3DLayer()
              width: 50,
              height: 500,
              depth: 50,
              resource: {
                primitive: "cylinder"
              },
              material: {
                color: "#FFD700"
              }
            }
          ]
        };

        var objectSymbolRenderer = {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: objectSymbol
        };
        
        var map = new Map({
          basemap: "topo-vector",
          ground: "world-elevation",
          layers: [featureLayer]
        });

        // create 2D view and and set active
        appConfig.mapView = createView(initialViewParams, "2d");
        appConfig.mapView.map = map;
        appConfig.activeView = appConfig.mapView;
        appConfig.mapView.when(function(){
          appConfig.injuryLayerRend2D = featureLayer.renderer;
        })

        // create 3D view, won't initialize until container is set
        initialViewParams.container = null;
        initialViewParams.map = map;
        appConfig.sceneView = createView(initialViewParams, "3d");

        // switch the view between 2D and 3D each time the button is clicked
        switchButton.addEventListener("click", function() {
          switchView();
        });

        // Switches the view from 2D to 3D and vice versa
        function switchView() {
          var is3D = appConfig.activeView.type === "3d";
          var activeViewpoint = appConfig.activeView.viewpoint.clone();

          // remove the reference to the container for the previous view
          appConfig.activeView.container = null;

          if (is3D) {
            // if the input view is a SceneView, set the viewpoint on the
            // mapView instance. Set the container on the mapView and flag
            // it as the active view
            featureLayer.renderer = appConfig.injuryLayerRend2D;
            appConfig.mapView.viewpoint = activeViewpoint;
            appConfig.mapView.container = appConfig.container;
            appConfig.activeView = appConfig.mapView;
            switchButton.value = "3D";
            
          } else {
            //featureLayer.renderer = objectSymbolRenderer;
            appConfig.sceneView.viewpoint = activeViewpoint;
            appConfig.sceneView.container = appConfig.container;
            appConfig.activeView = appConfig.sceneView;
            switchButton.value = "2D";
            featureLayer.renderer = objectSymbolRenderer;
          }
        }

        // convenience function for creating a 2D or 3D view
        function createView(params, type) {
          var view;
          var is2D = type === "2d";
          if (is2D) {
            view = new MapView(params);
            return view;
          } else {
            view = new SceneView(params);
          }
          return view;
        }
      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div id="infoDiv">
      <input
        class="esri-component esri-widget--button esri-widget esri-interactive"
        type="button"
        id="switch-btn"
        value="3D"
      />
    </div>
  </body>
</html>

View solution in original post

1 Reply
RobertScheitlin__GISP
MVP Emeritus

George,

   In this sample I do not have any issue with that.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>Switch view from 2D to 3D - 4.15</title>

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

      #infoDiv {
        position: absolute;
        top: 15px;
        left: 60px;
      }

      #infoDiv input {
        border: none;
        box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 2px;
      }
    </style>

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

    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/views/SceneView",
        "esri/layers/FeatureLayer"
      ], function(Map, MapView, SceneView, FeatureLayer) {
        var switchButton = document.getElementById("switch-btn");

        var appConfig = {
          mapView: null,
          sceneView: null,
          activeView: null,
          container: "viewDiv" // use same container for views
        };

        var initialViewParams = {
          zoom: 12,
          center: [-122.43759993450347, 37.772798684981126],
          container: appConfig.container
        };
        var featureLayer = new FeatureLayer({
          url:
            "https://services.arcgis.com/Zs2aNLFN00jrS4gG/arcgis/rest/services/PedInjuryCounts_2013/FeatureServer/0"
        });
        
        // Create objectSymbol and add to renderer
        var objectSymbol = {
          type: "point-3d", // autocasts as new PointSymbol3D()
          symbolLayers: [
            {
              type: "object", // autocasts as new ObjectSymbol3DLayer()
              width: 50,
              height: 500,
              depth: 50,
              resource: {
                primitive: "cylinder"
              },
              material: {
                color: "#FFD700"
              }
            }
          ]
        };

        var objectSymbolRenderer = {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: objectSymbol
        };
        
        var map = new Map({
          basemap: "topo-vector",
          ground: "world-elevation",
          layers: [featureLayer]
        });

        // create 2D view and and set active
        appConfig.mapView = createView(initialViewParams, "2d");
        appConfig.mapView.map = map;
        appConfig.activeView = appConfig.mapView;
        appConfig.mapView.when(function(){
          appConfig.injuryLayerRend2D = featureLayer.renderer;
        })

        // create 3D view, won't initialize until container is set
        initialViewParams.container = null;
        initialViewParams.map = map;
        appConfig.sceneView = createView(initialViewParams, "3d");

        // switch the view between 2D and 3D each time the button is clicked
        switchButton.addEventListener("click", function() {
          switchView();
        });

        // Switches the view from 2D to 3D and vice versa
        function switchView() {
          var is3D = appConfig.activeView.type === "3d";
          var activeViewpoint = appConfig.activeView.viewpoint.clone();

          // remove the reference to the container for the previous view
          appConfig.activeView.container = null;

          if (is3D) {
            // if the input view is a SceneView, set the viewpoint on the
            // mapView instance. Set the container on the mapView and flag
            // it as the active view
            featureLayer.renderer = appConfig.injuryLayerRend2D;
            appConfig.mapView.viewpoint = activeViewpoint;
            appConfig.mapView.container = appConfig.container;
            appConfig.activeView = appConfig.mapView;
            switchButton.value = "3D";
            
          } else {
            //featureLayer.renderer = objectSymbolRenderer;
            appConfig.sceneView.viewpoint = activeViewpoint;
            appConfig.sceneView.container = appConfig.container;
            appConfig.activeView = appConfig.sceneView;
            switchButton.value = "2D";
            featureLayer.renderer = objectSymbolRenderer;
          }
        }

        // convenience function for creating a 2D or 3D view
        function createView(params, type) {
          var view;
          var is2D = type === "2d";
          if (is2D) {
            view = new MapView(params);
            return view;
          } else {
            view = new SceneView(params);
          }
          return view;
        }
      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div id="infoDiv">
      <input
        class="esri-component esri-widget--button esri-widget esri-interactive"
        type="button"
        id="switch-btn"
        value="3D"
      />
    </div>
  </body>
</html>