Select to view content in your preferred language

Zoom to filtered layer

2154
7
Jump to solution
12-12-2022 06:05 AM
Mento
by
Emerging Contributor

Hi All, I'm having an issue with zooming/panning to an extent after filtering the layer. So, my filter works perfectly however when I use a view.goTo() it sometimes zooms out and If I filter to a feature (i.e. different province) not in the current view, it doesn't move to that feature's extent. I basically want the extent to be focused on the filtered expression

<html>

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

  <title>
    Application
  </title>

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

    #viewDiv {
      float: right;
      padding: 0;
      margin: 0;
      height: 100%;
      width: 85%;
    }

    #feature-node {
      float: left;
      width: 15%;
      height: 100%;
      max-height: 100%;
      background: #eeeeee;
      padding: 1mm;
      border-style: solid;
      border-color: #b0aeab;
      border-width: thin;
    }

    #filterDiv {
      margin-top: 5px;
      border-style: solid;
      border-color: #b0aeab;
      border-width: thin;
      height: 17%;
    }

    .action {
      color: blue;
      cursor: pointer;
      text-decoration: underline;
    }

    #rings {
      padding: 10px;
    }

    .header {
      height: 30px;
      padding-top: 5px;
    }

    .menu {
      display: table;
      width: 100%;
      border: 1px solid black;
      border-right: none;
      box-sizing: border-box;
    }

    .menu>div {
      display: table-row;
      background-color: white;
    }

    .menu>div>div {
      border-right: 1px solid black;
      display: table-cell;
      text-align: center;
      vertical-align: middle;
    }

    .boxpadding {
      margin: 8px 0;
    }

    .float-container {
      display: flex;
      padding: 2px;
      gap: 8px;
      margin: 8px 0;
    }

    .float-child1 {
      flex: 1;
    }

    .float-child2 {
      flex: 2;
    }
  </style>

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

  <script>
    require([
      "esri/config",
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/GroupLayer",
      "esri/layers/FeatureLayer",
      "esri/widgets/LayerList",
      "esri/widgets/Slider",
      "esri/widgets/Expand",
      "esri/widgets/Legend",
      "esri/portal/Portal",
      "esri/identity/OAuthInfo",
      "esri/identity/IdentityManager",
      "esri/layers/VectorTileLayer",
      "esri/Basemap",
      "esri/widgets/BasemapGallery",
      "esri/widgets/Search",
      "esri/widgets/ScaleBar",
      "esri/layers/GraphicsLayer",
      "esri/views/draw/Draw",
      "esri/Graphic",
      "esri/geometry/geometryEngine",
      "esri/rest/locator",
      "esri/widgets/Home"
    ], (esriConfig, Map, MapView, GroupLayer, FeatureLayer, LayerList, Slider, Expand, Legend, Portal, OAuthInfo, IdentityManager, VectorTileLayer, Basemap, BasemapGallery, Search, ScaleBar, GraphicsLayer, Draw, Graphic, geometryEngine, locator, Home) => {
      const info = new OAuthInfo({
        appId: "",
        popup: false
      });
      IdentityManager.registerOAuthInfos([info]);
      IdentityManager.checkSignInStatus(info.portalUrl + "/sharing").then(() => {
        displayMap();
        document.getElementById("panel").style.visibility = "visible"
      }).catch(() => {
        document.getElementById("anonymousPanel").style.display = "block";
      });
      document.getElementById("sign-in").addEventListener("click", () => {
        document.getElementById("sign-in").style.display = "none";
        IdentityManager.getCredential(info.portalUrl + "/sharing");
      });

      function displayMap() {
        const portal = new Portal();
        portal.load().then(() => {
          const AVGHomeValues = new FeatureLayer({
            url: "https://services7.arcgis.com/N6e25NYZwW1P8EDW/arcgis/rest/services/Average_Home_Values/FeatureServer",
            title: "Average Home Values",
            outFields: ["AllRegions_CSDNAME"],
            minScale: 0,
            maxScale: 0,
            definitionExpression: "AllRegions_CSDNAME = 'Etobicoke' OR AllRegions_CSDNAME='Oakville' OR AllRegions_CSDNAME ='Victoria' OR AllRegions_CSDNAME = 'West Vancouver' OR AllRegions_CSDNAME = 'Etobicoke - Core' OR AllRegions_CSDNAME = 'Etobicoke - North' OR AllRegions_CSDNAME = 'Etobicoke - South'"
          });
          const AVGRentPaid = new FeatureLayer({
            url: "https://services7.arcgis.com/N6e25NYZwW1P8EDW/arcgis/rest/services/Average_Rent_Paid/FeatureServer",
            title: "Average Rent Paid",
            outFields: ["AllRegions_CSDNAME"],
            minScale: 0,
            maxScale: 0,
            definitionExpression: "AllRegions_CSDNAME = 'Etobicoke' OR AllRegions_CSDNAME='Oakville' OR AllRegions_CSDNAME ='Victoria' OR AllRegions_CSDNAME = 'West Vancouver' OR AllRegions_CSDNAME = 'Etobicoke - Core' OR AllRegions_CSDNAME = 'Etobicoke - North' OR AllRegions_CSDNAME = 'Etobicoke - South'"
          });

          const wealth = new GroupLayer({
            title: "Wealth",
            visible: true,
            layers: [AVGHomeValues, AVGRentPaid],
            opacity: 1,
            visibilityMode: "exclusive"
          });

          const lightgray = new VectorTileLayer({
            portalItem: {
              id: "9b8266886ff043b799a8ea39d8381060"
            }
          });
          const esribasemap = new Basemap({
            baseLayers: [lightgray],
            title: "Light Gray (no labels)",
            thumbnailUrl: Basemap.fromId("gray-vector").thumbnailUrl
          });
          const map = new Map({
            basemap: esribasemap,
            layers: [wealth]
          });
          const view = new MapView({
            center: [-79.3832, 43.6532],
            zoom: 10,
            container: "viewDiv",
            map: map
          });
          const basemapGallery = new BasemapGallery({
            view: view,
            source: [Basemap.fromId("gray-vector"), Basemap.fromId("hybrid"), esribasemap, Basemap.fromId("satellite"), Basemap.fromId("streets-navigation-vector")]
          });
          const bgExpand = new Expand({
            view: view,
            content: basemapGallery,
            expandTooltip: "Basemaps"
          });
          basemapGallery.watch("activeBasemap", () => {
            const mobileSize = view.heightBreakpoint === "xsmall" || view.widthBreakpoint === "xsmall";
            if (mobileSize) {
              bgExpand.collapse();
            }
          });
          const draw = new Draw({
            view: view,
          });


        
          var regions = {
            'Ontario': ['', 'Oakville', 'Etobicoke'],
            'British Columbia': ['', 'Victoria', 'West Vancouver']
          };
          var districts = {
            'Etobicoke': ['', 'Etobicoke', 'Etobicoke - Core', 'Etobicoke - North', 'Etobicoke - South'],
            'Victoria': ['', 'Victoria'],
            'West Vancouver': ['', 'West Vancouver'],
            'Oakville': ['', 'Oakville']
          };
          var regionOption = document.querySelector("#municipality");
          var districtOption = document.querySelector("#districtName");
          var provOption = document.querySelector("#region");
          createOption(provOption, Object.keys(regions));
          createOption(regionOption, regions[provOption.value]);
          provOption.addEventListener('change', function() {
            createOption(regionOption, regions[provOption.value]);
          });
          regionOption.addEventListener('change', function() {
            createOption(districtOption, districts[regionOption.value]);
          });
          document.getElementById("districtName").addEventListener("change", () => {
            view.allLayerViews.forEach((lv) => {
              if (lv.layer.type === "feature") {
                if (document.getElementById("districtName").value != " ") {
                  lv.filter = {
                    where: "AllRegions_CSDNAME = '" +
                      document.getElementById("districtName").value +
                      "'"
                  };
                  lv.watch("updating", function(val) {
                    if (!val) {
                      lv.queryExtent().then(function(response) {
                        view.goTo(response.extent);
                      });
                    };
                  });
                }
              }
            });
          });

          function createOption(dropDown, options) {
            dropDown.innerHTML = '';
            options.forEach(function(value) {
              dropDown.innerHTML += '<option name="' + value + '">' + value + '</option>';
            });
          };

          

      

          function defineActions(event) {
            const item = event.item;
            if (item.layer.type === "group" & item.title != "Boundaries") {
              const slider = new Slider({
                min: 0,
                max: 1,
                precision: 2,
                values: [1],
                visibleElements: {
                  labels: true,
                  rangeLabels: true
                }
              });
              item.panel = {
                content: slider,
                className: "esri-icon-sliders-horizontal",
                title: "Change Opacity"
              };
              slider.on("thumb-drag", (event) => {
                const {
                  value
                } = event;
                item.layer.opacity = value;
              });
            }
          }
          view.when(() => {
            const layerList = new LayerList({
              view: view,
              listItemCreatedFunction: defineActions,
            });
            layerList.on("trigger-action", (event) => {
              const id = event.action.id;
              if (id === "toggle-labels") {
                if (Neighbourhoods.labelsVisible == false) {
                  Neighbourhoods.labelsVisible = true;
                } else {
                  Neighbourhoods.labelsVisible = false;
                }
              }
            });
            const legend = new Expand({
              content: new Legend({
                view: view,
              }),
              view: view,
              expandTooltip: "Legend"
            });
            view.ui.add(bgExpand, "top-left");
            view.ui.add(legend, "top-left");
            view.ui.add(layerList, "top-left");
          });
        });
      }
    });
  </script>
</head>

<body>
  <div id="anonymousPanel" style="display: none; padding: 5px; text-align: center;">
    <span id="sign-in" class="action">Sign In</span> to view the content
  </div>
  <div id="panel" style="visibility:hidden;">
    <div id="feature-node" class="esri-widget">
      <div id="filterDiv" class="esri-widget">
        <div id="filterwidget" class="esri-widget">
          <h3 align="center">Filter by Region</h3>
          <div class="float-container">
            <div class="float-child1">
              <div>Province: </div>
            </div>
            <div class="float-child2">
              <div id="province" class="esri-widget">
                <select id="region" style="width:110px;">
                </select>
              </div>
            </div>
          </div>
          <div class="float-container">
            <div class="float-child1">
              <div>Region: </div>
            </div>
            <div class="float-child2">
              <div id="municipalities" class="esri-widget">
                <select id="municipality" style="width: 110px;"></select>
              </div>
            </div>
          </div>
          <div class="float-container">
            <div class="float-child1">
              <div>Districts: </div>
            </div>
            <div class="float-child2">
              <div id="districtsFilter" class="esri-widget">
                <select id="districtName" style="width: 110px;"></select>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div id="viewDiv"></div>
</body>

</html>

  

@Sage_Wall @ReneRubalcava @UndralBatsukh @JeffreyWilkerson 

0 Kudos
1 Solution

Accepted Solutions
Sage_Wall
Esri Regular Contributor

I didn't seem to have access to those feature layers your using @Mento otherwise I would have modified your example directly, but I did make a Codepen that shows how the definition expression on two feature layers can be set with an HTML select element.  I hope this helps.

I really think you just need to switch your logic away from using the client side layer views especially if you are querying features that are not visible in the view.  Something like this maybe (code wasn't run or debugged), changing out the layer view filters for feature layer definition queries.

document.getElementById("districtName").addEventListener("change", () => {
    map.layers.forEach((layer) => {
        if (layer.type === "feature") {
            if (document.getElementById("districtName").value != " ") {
                layer.definitionQuery = "AllRegions_CSDNAME = '" +
                    document.getElementById("districtName").value +
                    "'";
                layer.queryExtent().then(function (results) {
                  view.goTo(results.extent);
                });
            }
        }
    });
});

,  

View solution in original post

7 Replies
Sage_Wall
Esri Regular Contributor

Hi @Mento ,

The layer view provides access to a layer's features that are currently displayed in the view. So when you query for items that are outside the current extent of the view no results are returned.  I think if you modify your code to query the feature layers directly, instead of the layer views, you'll be good to go.

0 Kudos
Sage_Wall
Esri Regular Contributor

I didn't seem to have access to those feature layers your using @Mento otherwise I would have modified your example directly, but I did make a Codepen that shows how the definition expression on two feature layers can be set with an HTML select element.  I hope this helps.

I really think you just need to switch your logic away from using the client side layer views especially if you are querying features that are not visible in the view.  Something like this maybe (code wasn't run or debugged), changing out the layer view filters for feature layer definition queries.

document.getElementById("districtName").addEventListener("change", () => {
    map.layers.forEach((layer) => {
        if (layer.type === "feature") {
            if (document.getElementById("districtName").value != " ") {
                layer.definitionQuery = "AllRegions_CSDNAME = '" +
                    document.getElementById("districtName").value +
                    "'";
                layer.queryExtent().then(function (results) {
                  view.goTo(results.extent);
                });
            }
        }
    });
});

,  

Mento
by
Emerging Contributor

Hi @Sage_Wall  I implemented the above code it goes to the extent but it does not filter it even though im doing layer.definitionExpression = ...

 

0 Kudos
Sage_Wall
Esri Regular Contributor

Sorry to head that.  I really wish the feature services above were accessible to me and it would be easier to help debug.  I'd suggest setting some breakpoints in the event handlers and step though the code looking at the map and layer properties.  It's likely just a typo or something, but stepping through the code line by line should help narrow down the issue.  

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

Sage is completely right. If the filter is applied to features that are outside of the current extent then these features are not available in the layer view. So you have to query the layer to get the extent. 

However, if  you must use layerView for performance reasons... I think you can do the something like this. Run the queryExtent on LayerView and if the LayerView does not return an extent then run the queryExtent on the layer so this way you are getting features that are not available in the layer view. So you can do something like this. 

 

 

 

          const query = {
            where:  layerView.filter.where,
            returnGeometry: true,
          };
          let {extent, count} = await layerView.queryExtent(query);
          if (count == 0){
            let {extent, count} = await layer.queryExtent(query);
            view.goTo(extent);
            console.log("layer query extent")
          } else {
            view.goTo(extent);
            console.log("layerview query extent");
          }

 

 

 

 

Mento
by
Emerging Contributor

@UndralBatsukh I Implemented the following code, but my application stops loading, i think its to do with the let statements

0 Kudos
UndralBatsukh
Esri Regular Contributor

The await operators can only be used inside async functions. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

Or you can change the code the way you had it and use nested promises. 

 

0 Kudos