Home > database >  Query statistics by geometry in Scene Layer View using JavaScript API
Query statistics by geometry in Scene Layer View using JavaScript API

Time:10-12

I am using client-side feature queries which are useful in situations where the application should deliver immediate results in response to user input. With queries on the LayerView there is no round-trip to the server. Users can draw a polygon, polyline or point geometry which is then used as the spatial geometry parameter in the query.

Before running the query on the the SceneLayerView we define what statistics we want to receive on the matching features. This is achieved assigning a StatisticDefinition array to outStatistics and calling queryFeatures on the SceneLayerView. Below is my approach to achieve this , I am looking for other approaches to do the same.

<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>
      SceneLayerView - query statistics by geometry | Sample | ArcGIS API for
      JavaScript 4.21
    </title>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }

      .esri-ui-top-right {
        max-height: 100%;
      }

      #queryDiv,
      #resultDiv {
        min-width: 250px;
        font-size: 14px;
        padding: 10px;
        display: none;
        overflow-y: auto;
        overflow-x: hidden;
      }

      .geometry-options {
        display: flex;
        flex-direction: row;
      }
      .geometry-button {
        flex: 1;
        border-style: solid;
        border-width: 1px;
        border-image: none;
      }
      .geometry-button-selected {
        background: #4c4c4c;
        color: #fff;
      }

      #bufferNum {
        width: 90%;
        margin: 2.5em auto 0;
      }

      .count {
        white-space: nowrap;
        font-size: 14px;
        font-weight: bold;
        display: inline-block;
      }
    </style>
    <!-- Load the Chart.js library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>

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

    <script>
      require([
        "esri/WebScene",
        "esri/views/SceneView",
        "esri/layers/GraphicsLayer",
        "esri/widgets/Sketch/SketchViewModel",
        "esri/widgets/Slider",
        "esri/geometry/geometryEngine",
        "esri/Graphic",
        "esri/core/promiseUtils"
      ], (
        WebScene,
        SceneView,
        GraphicsLayer,
        SketchViewModel,
        Slider,
        geometryEngine,
        Graphic,
        promiseUtils
      ) => {
        // Load webscene and display it in a SceneView
        const webscene = new WebScene({
          portalItem: {
            id: "fb5948b2bb76439792786000b942e5b7"
          }
        });
        // create the SceneView
        const view = new SceneView({
          container: "viewDiv",
          map: webscene
        });
        window.view = view;
        // add a GraphicsLayer for the sketches and the buffer
        const sketchLayer = new GraphicsLayer();
        const bufferLayer = new GraphicsLayer();
        view.map.addMany([bufferLayer, sketchLayer]);

        let sceneLayer = null;
        let sceneLayerView = null;
        let bufferSize = 0;

        // Assign scene layer once webscene is loaded and initialize UI
        webscene.load().then(() => {
          sceneLayer = webscene.layers.find((layer) => {
            return layer.title === "Helsinki textured buildings";
          });
          sceneLayer.outFields = ["buildingMaterial", "yearCompleted"];

          view.whenLayerView(sceneLayer).then((layerView) => {
            sceneLayerView = layerView;
            queryDiv.style.display = "block";
          });
        });

        view.ui.add([queryDiv], "bottom-left");
        view.ui.add([resultDiv], "top-right");

        // use SketchViewModel to draw polygons that are used as a query
        let sketchGeometry = null;
        const sketchViewModel = new SketchViewModel({
          layer: sketchLayer,
          defaultUpdateOptions: {
            tool: "reshape",
            toggleToolOnClick: false
          },
          view: view,
          defaultCreateOptions: { hasZ: false }
        });

        sketchViewModel.on("create", (event) => {
          if (event.state === "complete") {
            sketchGeometry = event.graphic.geometry;
            runQuery();
          }
        });

        sketchViewModel.on("update", (event) => {
          if (event.state === "complete") {
            sketchGeometry = event.graphics[0].geometry;
            runQuery();
          }
        });
        // draw geometry buttons - use the selected geometry to sktech
        const pointBtn = document.getElementById("point-geometry-button");
        const lineBtn = document.getElementById("line-geometry-button");
        const polygonBtn = document.getElementById("polygon-geometry-button");
        pointBtn.addEventListener("click", geometryButtonsClickHandler);
        lineBtn.addEventListener("click", geometryButtonsClickHandler);
        polygonBtn.addEventListener("click", geometryButtonsClickHandler);
        function geometryButtonsClickHandler(event) {
          const geometryType = event.target.value;
          clearGeometry();
          sketchViewModel.create(geometryType);
        }

        const bufferNumSlider = new Slider({
          container: "bufferNum",
          min: 0,
          max: 500,
          steps: 1,
          visibleElements: {
            labels: true
          },
          precision: 0,
          labelFormatFunction: (value, type) => {
            return `${value.toString()}m`;
          },
          values: [0]
        });
        // get user entered values for buffer
        bufferNumSlider.on(
          ["thumb-change", "thumb-drag"],
          bufferVariablesChanged
        );
        function bufferVariablesChanged(event) {
          bufferSize = event.value;
          runQuery();
        }
        // Clear the geometry and set the default renderer
        const clearGeometryBtn = document.getElementById("clearGeometry");
        clearGeometryBtn.addEventListener("click", clearGeometry);

        // Clear the geometry and set the default renderer
        function clearGeometry() {
          sketchGeometry = null;
          sketchViewModel.cancel();
          sketchLayer.removeAll();
          bufferLayer.removeAll();
          clearHighlighting();
          clearCharts();
          resultDiv.style.display = "none";
        }

        // set the geometry query on the visible SceneLayerView
        const debouncedRunQuery = promiseUtils.debounce(() => {
          if (!sketchGeometry) {
            return;
          }

          resultDiv.style.display = "block";
          updateBufferGraphic(bufferSize);
          return promiseUtils.eachAlways([
            queryStatistics(),
            updateSceneLayer()
          ]);
        });

        function runQuery() {
          debouncedRunQuery().catch((error) => {
            if (error.name === "AbortError") {
              return;
            }

            console.error(error);
          });
        }

        // Set the renderer with objectIds
        let highlightHandle = null;
        function clearHighlighting() {
          if (highlightHandle) {
            highlightHandle.remove();
            highlightHandle = null;
          }
        }

        function highlightBuildings(objectIds) {
          // Remove any previous highlighting
          clearHighlighting();
          const objectIdField = sceneLayer.objectIdField;
          document.getElementById("count").innerHTML = objectIds.length;

          highlightHandle = sceneLayerView.highlight(objectIds);
        }

        // update the graphic with buffer
        function updateBufferGraphic(buffer) {
          // add a polygon graphic for the buffer
          if (buffer > 0) {
            const bufferGeometry = geometryEngine.geodesicBuffer(
              sketchGeometry,
              buffer,
              "meters"
            );
            if (bufferLayer.graphics.length === 0) {
              bufferLayer.add(
                new Graphic({
                  geometry: bufferGeometry,
                  symbol: sketchViewModel.polygonSymbol
                })
              );
            } else {
              bufferLayer.graphics.getItemAt(0).geometry = bufferGeometry;
            }
          } else {
            bufferLayer.removeAll();
          }
        }

        function updateSceneLayer() {
          const query = sceneLayerView.createQuery();
          query.geometry = sketchGeometry;
          query.distance = bufferSize;
          return sceneLayerView.queryObjectIds(query).then(highlightBuildings);
        }

        let yearChart = null;
        let materialChart = null;

        function queryStatistics() {
          const statDefinitions = [
            {
              onStatisticField:
                "CASE WHEN buildingMaterial = 'concrete or lightweight concrete' THEN 1 ELSE 0 END",
              outStatisticFieldName: "material_concrete",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN buildingMaterial = 'brick' THEN 1 ELSE 0 END",
              outStatisticFieldName: "material_brick",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN buildingMaterial = 'wood' THEN 1 ELSE 0 END",
              outStatisticFieldName: "material_wood",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN buildingMaterial = 'steel' THEN 1 ELSE 0 END",
              outStatisticFieldName: "material_steel",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN buildingMaterial IN ('concrete or lightweight concrete', 'brick', 'wood', 'steel') THEN 0 ELSE 1 END",
              outStatisticFieldName: "material_other",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '1850' AND yearCompleted <= '1899') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_1850",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '1900' AND yearCompleted <= '1924') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_1900",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '1925' AND yearCompleted <= '1949') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_1925",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '1950' AND yearCompleted <= '1974') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_1950",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '1975' AND yearCompleted <= '1999') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_1975",
              statisticType: "sum"
            },
            {
              onStatisticField:
                "CASE WHEN (yearCompleted >= '2000' AND yearCompleted <= '2015') THEN 1 ELSE 0 END",
              outStatisticFieldName: "year_2000",
              statisticType: "sum"
            }
          ];
          const query = sceneLayerView.createQuery();
          query.geometry = sketchGeometry;
          query.distance = bufferSize;
          query.outStatistics = statDefinitions;

          return sceneLayerView.queryFeatures(query).then((result) => {
            const allStats = result.features[0].attributes;
            updateChart(materialChart, [
              allStats.material_concrete,
              allStats.material_brick,
              allStats.material_wood,
              allStats.material_steel,
              allStats.material_other
            ]);
            updateChart(yearChart, [
              allStats.year_1850,
              allStats.year_1900,
              allStats.year_1925,
              allStats.year_1950,
              allStats.year_1975,
              allStats.year_2000
            ]);
          }, console.error);
        }

        // Updates the given chart with new data
        function updateChart(chart, dataValues) {
          chart.data.datasets[0].data = dataValues;
          chart.update();
        }

        function createYearChart() {
          const yearCanvas = document.getElementById("year-chart");
          yearChart = new Chart(yearCanvas.getContext("2d"), {
            type: "horizontalBar",
            data: {
              labels: [
                "1850-1899",
                "1900-1924",
                "1925-1949",
                "1950-1974",
                "1975-1999",
                "2000-2015"
              ],
              datasets: [
                {
                  label: "Build year",
                  backgroundColor: "#149dcf",
                  stack: "Stack 0",
                  data: [0, 0, 0, 0, 0, 0]
                }
              ]
            },
            options: {
              responsive: false,
              legend: {
                display: false
              },
              title: {
                display: true,
                text: "Build year"
              },
              scales: {
                xAxes: [
                  {
                    stacked: true,
                    ticks: {
                      beginAtZero: true,
                      precision: 0
                    }
                  }
                ],
                yAxes: [
                  {
                    stacked: true
                  }
                ]
              }
            }
          });
        }
        function createMaterialChart() {
          const materialCanvas = document.getElementById("material-chart");
          materialChart = new Chart(materialCanvas.getContext("2d"), {
            type: "doughnut",
            data: {
              labels: ["Concrete", "Brick", "Wood", "Steel", "Other"],
              datasets: [
                {
                  backgroundColor: [
                    "#FD7F6F",
                    "#7EB0D5",
                    "#B2E061",
                    "#BD7EBE",
                    "#FFB55A"
                  ],
                  borderWidth: 0,
                  data: [0, 0, 0, 0, 0]
                }
              ]
            },
            options: {
              responsive: false,
              cutoutPercentage: 35,
              legend: {
                position: "bottom"
              },
              title: {
                display: true,
                text: "Building Material"
              }
            }
          });
        }

        function clearCharts() {
          updateChart(materialChart, [0, 0, 0, 0, 0]);
          updateChart(yearChart, [0, 0, 0, 0, 0, 0]);
          document.getElementById("count").innerHTML = 0;
        }

        createYearChart();
        createMaterialChart();
      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div id="queryDiv" class="esri-widget">
      <b>Query by geometry</b><br />
      <br />Draw a geometry to query by:
      <div class="geometry-options">
        <button
          class="esri-widget--button esri-icon-map-pin geometry-button"
          id="point-geometry-button"
          value="point"
          title="Query by point"
        ></button>
        <button
          class="esri-widget--button esri-icon-polyline geometry-button"
          id="line-geometry-button"
          value="polyline"
          title="Query by line"
        ></button>
        <button
          class="esri-widget--button esri-icon-polygon geometry-button"
          id="polygon-geometry-button"
          value="polygon"
          title="Query by polygon"
        ></button>
      </div>
      <br />
      <div class="tooltip">
        <label for="bufferNum">Set a geometry buffer size:</label>
        <div id="bufferNum"></div>
      </div>
      <br />
      <button class="esri-button" id="clearGeometry" type="button">
        Clear
      </button>
    </div>

    <div id="resultDiv" class="esri-widget">
      <div class="count">
        Selected Buildings:
        <div class="count" id="count">0</div>
      </div>
      <div class="charts">
        <div>
          <canvas id="year-chart" height="250" width="260" />
        </div>
        <div>
          <canvas id="material-chart" width="250" height="300" />
        </div>
      </div>
    </div>
  </body>
</html>

CodePudding user response:

I am using this sample which shows how a FeatureEffect can be used to highlight features in a CSVLayerView and select the corresponding rows in a FeatureTable. A rectangle can be drawn on the view by clicking on the Select features by rectangle button and drawing a rectangle on the map.

<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />


    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.21/esri/themes/light/main.css"
    />
    <script src="https://js.arcgis.com/4.21/"></script>
    <style>
      html,
      body {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }

      #viewDiv {
        height: 50%;
        width: 100%;
      }

      .container {
        height: 50%;
        width: 100%;
      }
    </style>

    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/layers/CSVLayer",
        "esri/layers/FeatureLayer",
        "esri/layers/GraphicsLayer",
        "esri/widgets/Sketch/SketchViewModel",
        "esri/Graphic",
        "esri/widgets/FeatureTable",
        "esri/Basemap",
        "esri/geometry/geometryEngineAsync"
      ], function (
        Map,
        MapView,
        CSVLayer,
        FeatureLayer,
        GraphicsLayer,
        SketchViewModel,
        Graphic,
        FeatureTable,
        Basemap,
        geometryEngineAsync
      ) {
        // Use the states featurealayer as a basemap
        const states = new FeatureLayer({
          url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2",
          renderer: {
            type: "simple",
            symbol: {
              type: "simple-fill",
              color: "#f0ebe4",
              outline: {
                color: "#DCDCDC",
                width: "0.5px"
              }
            }
          },
          spatialReference: {
            wkid: 102003
          },
          effect: "drop-shadow(-10px, 10px, 6px gray)"
        });

        // national parks csv layer
        const csvLayer = new CSVLayer({
          url: "https://ubatsukh.github.io/arcgis-js-api-demos/devsummit2021/csvLayer-nps/data/nps_establishments.csv",
          delimiter: ",",
          popupTemplate: {
            title: "{unit_name}",
            content: "Established on <b>{date_est}</b> <br/><br/> {description}"
          },
          renderer: setRenderer()
        });

        let csvLayerView;
        csvLayer
          .when(() => {
            view.whenLayerView(csvLayer).then(function (layerView) {
              csvLayerView = layerView;
            });
          })
          .catch(errorCallback);

        const map = new Map({
          basemap: new Basemap({
            baseLayers: [states]
          }),
          layers: [csvLayer]
        });

        // initialize the view with USA Contiguous Albers Equal Area Conic
        // projection and set the extent to the states
        const view = new MapView({
          container: "viewDiv",
          map: map,
          extent: {
            type: "extent",
            spatialReference: {
              wkid: 102003
            },
            xmax: 2275062,
            xmin: -2752064,
            ymax: 1676207,
            ymin: -1348080
          },
          constraints: {
            snapToZoom: false,
            minScale: 50465153
          },
          spatialReference: {
            wkid: 102003
          },
          background: {
            color: "white"
          }
        });

        // create a new instance of a FeatureTable
        const featureTable = new FeatureTable({
          view: view,
          layer: csvLayer,
          highlightOnRowSelectEnabled: false,
          fieldConfigs: [
            {
              name: "unit_name",
              label: "Name"
            },
            {
              name: "state",
              label: "State"
            },
            {
              name: "region",
              label: "Region"
            },
            {
              name: "unit_type",
              label: "Type"
            },
            {
              name: "created_by",
              label: "Created by"
            },
            {
              name: "date_est",
              label: "Established date"
            },
            {
              name: "description",
              label: "Description"
            },
            {
              name: "caption",
              label: "Caption"
            }
          ],
          container: document.getElementById("tableDiv")
        });

        // this array will keep track of selected feature objectIds to
        // sync the layerview effects and feature table selection
        let features = [];

        // Listen for the table's selection-change event
        featureTable.on("selection-change", (changes) => {
          // if the feature is unselected then remove the objectId
          // of the removed feature from the features array
          changes.removed.forEach((item) => {
            const data = features.find((data) => {
              return data === item.objectId;
            });
            if (data) {
              features.splice(features.indexOf(data), 1);
            }
          });

          // If the selection is added, push all added selections to array
          changes.added.forEach((item) => {
            features.push(item.objectId);
          });

          // set excluded effect on the features that are not selected in the table
          csvLayerView.effect = {
            filter: {
              objectIds: features
            },
            excludedEffect: "blur(5px) grayscale(90%) opacity(40%)"
          };
        });

        // polygonGraphicsLayer will be used by the sketchviewmodel
        // show the polygon being drawn on the view
        const polygonGraphicsLayer = new GraphicsLayer();
        map.add(polygonGraphicsLayer);

        // add the select by rectangle button the view
        view.ui.add("select-by-rectangle", "top-left");
        const selectButton = document.getElementById("select-by-rectangle");

        // click event for the select by rectangle button
        selectButton.addEventListener("click", () => {
          view.popup.close();
          sketchViewModel.create("rectangle");
        });

        // add the clear selection button the view
        view.ui.add("clear-selection", "top-left");
        document
          .getElementById("clear-selection")
          .addEventListener("click", () => {
            featureTable.clearSelection();
            featureTable.filterGeometry = null;
            polygonGraphicsLayer.removeAll();
          });

        // create a new sketch view model set its layer
        const sketchViewModel = new SketchViewModel({
          view: view,
          layer: polygonGraphicsLayer
        });

        // Once user is done drawing a rectangle on the map
        // use the rectangle to select features on the map and table
        sketchViewModel.on("create", async (event) => {
          if (event.state === "complete") {
            // this polygon will be used to query features that intersect it
            const geometries = polygonGraphicsLayer.graphics.map(function (
              graphic
            ) {
              return graphic.geometry;
            });
            const queryGeometry = await geometryEngineAsync.union(
              geometries.toArray()
            );
            selectFeatures(queryGeometry);
          }
        });

        // This function is called when user completes drawing a rectangle
        // on the map. Use the rectangle to select features in the layer and table
        function selectFeatures(geometry) {
          if (csvLayerView) {
            // create a query and set its geometry parameter to the
            // rectangle that was drawn on the view
            const query = {
              geometry: geometry,
              outFields: ["*"]
            };

            // query graphics from the csv layer view. Geometry set for the query
            // can be polygon for point features and only intersecting geometries are returned
            csvLayerView
              .queryFeatures(query)
              .then((results) => {
                if (results.features.length === 0) {
                  clearSelection();
                } else {
                  // pass in the query results to the table by calling its selectRows method.
                  // This will trigger FeatureTable's selection-change event
                  // where we will be setting the feature effect on the csv layer view
                  featureTable.filterGeometry = geometry;
                  featureTable.selectRows(results.features);
                }
              })
              .catch(errorCallback);
          }
        }

        function errorCallback(error) {
          console.log("error happened:", error.message);
        }

        // this is called from CSVLayer constructor
        // tree CIM symbol
        function setRenderer() {
          return {
            type: "simple",
            symbol: {
              type: "cim",
              data: {
                type: "CIMSymbolReference",
                symbol: {
                  type: "CIMPointSymbol",
                  symbolLayers: [
                    {
                      type: "CIMVectorMarker",
                      enable: true,
                      anchorPointUnits: "Relative",
                      dominantSizeAxis3D: "Y",
                      size: 15.75,
                      billboardMode3D: "FaceNearPlane",
                      frame: {
                        xmin: 0,
                        ymin: 0,
                        xmax: 21,
                        ymax: 21
                      },
                      markerGraphics: [
                        {
                          type: "CIMMarkerGraphic",
                          geometry: {
                            rings: [
                              [
                                [15, 15],
                                [12, 15],
                                [16, 10],
                                [13, 10],
                                [17, 5],
                                [11, 5],
                                [11, 2],
                                [10, 2],
                                [10, 5],
                                [4, 5],
                                [8, 10],
                                [5, 10],
                                [9, 15],
                                [6, 15],
                                [10.5, 19],
                                [15, 15]
                              ]
                            ]
                          },
                          symbol: {
                            type: "CIMPolygonSymbol",
                            symbolLayers: [
                              {
                                type: "CIMSolidStroke",
                                enable: true,
                                capStyle: "Round",
                                joinStyle: "Round",
                                lineStyle3D: "Strip",
                                miterLimit: 10,
                                width: 0,
                                color: [0, 0, 0, 255]
                              },
                              {
                                type: "CIMSolidFill",
                                enable: true,
                                color: [0, 160, 0, 255]
                              }
                            ]
                          }
                        }
                      ],
                      scaleSymbolsProportionally: true,
                      respectFrame: true
                    },
                    {
                      type: "CIMVectorMarker",
                      enable: true,
                      colorLocked: true,
                      anchorPointUnits: "Relative",
                      dominantSizeAxis3D: "Y",
                      size: 8,
                      billboardMode3D: "FaceNearPlane",
                      frame: {
                        xmin: -5,
                        ymin: -5,
                        xmax: 5,
                        ymax: 5
                      },
                      markerGraphics: [
                        {
                          type: "CIMMarkerGraphic",
                          geometry: {
                            rings: [
                              [
                                [0, 5],
                                [0.87, 4.92],
                                [1.71, 4.7],
                                [2.5, 4.33],
                                [3.21, 3.83],
                                [3.83, 3.21],
                                [4.33, 2.5],
                                [4.7, 1.71],
                                [4.92, 0.87],
                                [5, 0],
                                [4.92, -0.87],
                                [4.7, -1.71],
                                [4.33, -2.5],
                                [3.83, -3.21],
                                [3.21, -3.83],
                                [2.5, -4.33],
                                [1.71, -4.7],
                                [0.87, -4.92],
                                [0, -5],
                                [-0.87, -4.92],
                                [-1.71, -4.7],
                                [-2.5, -4.33],
                                [-3.21, -3.83],
                                [-3.83, -3.21],
                                [-4.33, -2.5],
                                [-4.7, -1.71],
                                [-4.92, -0.87],
                                [-5, 0],
                                [-4.92, 0.87],
                                [-4.7, 1.71],
                                [-4.33, 2.5],
                                [-3.83, 3.21],
                                [-3.21, 3.83],
                                [-2.5, 4.33],
                                [-1.71, 4.7],
                                [-0.87, 4.92],
                                [0, 5]
                              ]
                            ]
                          },
                          symbol: {
                            type: "CIMPolygonSymbol",
                            symbolLayers: [
                              {
                                type: "CIMSolidStroke",
                                enable: true,
                                capStyle: "Round",
                                joinStyle: "Round",
                                lineStyle3D: "Strip",
                                miterLimit: 10,
                                width: 0.5,
                                color: [167, 169, 172, 255]
                              },
                              {
                                type: "CIMSolidFill",
                                enable: true,
                                color: [255, 255, 255, 255]
                              }
                            ]
                          }
                        }
                      ],
                      scaleSymbolsProportionally: true,
                      respectFrame: true
                    }
                  ],
                  haloSize: 1,
                  scaleX: 1,
                  angleAlignment: "Display"
                }
              }
            }
          };
        }
      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div
      id="select-by-rectangle"
      class="esri-widget esri-widget--button esri-widget esri-interactive"
      title="Select features by rectangle"
    >
      <span class="esri-icon-checkbox-unchecked"></span>
    </div>
    <div
      id="clear-selection"
      class="esri-widget esri-widget--button esri-widget esri-interactive"
      title="Clear selection"
    >
      <span class="esri-icon-erase"></span>
    </div>
    <div class="container">
      <div id="tableDiv"></div>
    </div>
  </body>
</html>

CodePudding user response:

The following will be useful , if you want to query all features. It can be convenient and useful for users to zoom to the extent of all features in a FeatureLayer once an app loads, when a layer is added to the map, or when a layer's definitionExpression is updated.

The FeatureLayer API provides a method called queryExtent(), which allows you to calculate the full extent of features at runtime that satisfy a given query. If no query parameters are set, then the method queries for the extent of all features in the layer according to its definitionExpression.

<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
 

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

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

    <script>
      require([
        "esri/Map",
        "esri/views/SceneView",
        "esri/layers/FeatureLayer",
        "esri/widgets/LayerList",
        "esri/core/Collection"
      ], (Map, SceneView, FeatureLayer, LayerList, Collection) => {
        const featureLayer = new FeatureLayer({
          outFields: ["STATION_NAME", "COUNTRY", "TEMP"],
          portalItem: {
            // autocasts as new PortalItem()
            id: "3a177da3f6524d61980fb41125b2349c"
          },
          title: "Temperature on Jan, 4, 2017"
        });

        // When the layer is loaded, query for the extent
        // of all features in the layer that satisfy the
        // definitionExpression. Then set the view's
        // extent to the returned extent of all features.

        featureLayer.when(() => {
          featureLayer.definitionExpression = createDefinitionExpression("");
          zoomToLayer(featureLayer);
        });

        const map = new Map({
          basemap: "dark-gray-vector",
          layers: [featureLayer]
        });

        const view = new SceneView({
          container: "viewDiv",
          map: map
        });

        const layerList = new LayerList({
          view: view,
          listItemCreatedFunction: createActions
        });
        view.ui.add(layerList, "top-right");

        // definitionExpressions used by each action
        // listed in the LayerList

        const expressions = new Collection([
          {
            id: "75 ",
            expression: "TEMP > 75"
          },
          {
            id: "50-75",
            expression: "TEMP > 50 AND TEMP <=75"
          },
          {
            id: "25-50",
            expression: "TEMP > 25 AND TEMP <=50"
          },
          {
            id: "25-",
            expression: "TEMP <= 25"
          },
          {
            id: "arctic-circle",
            expression: "LATITUDE >= 66.5"
          },
          {
            id: "north-temperate-zone",
            expression: "LATITUDE < 66.5 AND LATITUDE >= 23.5"
          },
          {
            id: "torrid-zone",
            expression: "LATITUDE < 23.5 AND LATITUDE >= -23.5"
          }
        ]);

        // When an action is triggered, the definitionExpression
        // is set on the layer and the view's extent updates
        // to match the features visible in the layer

        layerList.on("trigger-action", (event) => {
          const actionId = event.action.id;
          const layer = event.item.layer;

          const subExpression = expressions.find((item) => {
            return item.id === actionId;
          }).expression;

          const definitionExpression =
            createDefinitionExpression(subExpression);
          layer.definitionExpression = definitionExpression;

          zoomToLayer(layer);
        });

        function createActions(event) {
          const item = event.item;

          item.actionsOpen = true;
          item.actionsSections = [
            [
              {
                title: "> 75°F",
                className: "esri-icon-zoom-out-fixed",
                id: "75 "
              },
              {
                title: "50°-75°F",
                className: "esri-icon-zoom-out-fixed",
                id: "50-75"
              },
              {
                title: "25°-50°F",
                className: "esri-icon-zoom-out-fixed",
                id: "25-50"
              },
              {
                title: "< 25°F",
                className: "esri-icon-zoom-out-fixed",
                id: "25-"
              }
            ],
            [
              {
                title: "Above Arctic Circle",
                className: "esri-icon-zoom-out-fixed",
                id: "arctic-circle"
              },
              {
                title: "North Temperate Zone",
                className: "esri-icon-zoom-out-fixed",
                id: "north-temperate-zone"
              },
              {
                title: "Torrid Zone",
                className: "esri-icon-zoom-out-fixed",
                id: "torrid-zone"
              }
            ]
          ];
        }

        // Appends a definitionExpression to a base expression
        // the base expression only returns freatures in
        // Canada, USA, and Mexico. It excludes some US territories
        // located on the other side of the dateline

        function createDefinitionExpression(subExpression) {
          const baseExpression =
            "( COUNTRY LIKE '%United States Of America%' OR "  
            "COUNTRY LIKE '           
  • Related