Query visible graphics client-side using Webmap

2523
8
Jump to solution
04-01-2018 04:57 PM
Raul_Jimenez
Esri Contributor

I'm using JS API 4.6 and I'm trying to show in a table all graphics been displayed in the current extent directly querying the client/view.

You can find a live sample of the skeleton I'm using here or directly in here:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <title>Query graphics on webmap</title>

    <link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
    <script>
    var dojoConfig = {
        has: {
            "esri-featurelayer-webgl": 1
        }
    };
    </script>
    <script src="https://js.arcgis.com/4.6/"></script>

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

    <script>
    require([
        "esri/WebMap",
        "esri/views/MapView",
        "esri/widgets/LayerList",
        "esri/widgets/Legend",
        "esri/geometry/geometryEngine",
        "esri/core/watchUtils",
        "esri/tasks/support/Query",
        "dojo/domReady!"
    ],
    function(
        WebMap,
        MapView,
        LayerList,
        Legend,
        geometryEngine,
        watchUtils,
        Query
    ) {

        webmap = new WebMap({
            portalItem: { 
                id: "19d21df5e87f4f5a9aa5b8d678b174d6"
            }
        });
        view = new MapView({
            container: "viewDiv",
            map: webmap,
            popup: {
                dockEnabled: true,
                dockOptions: {
                    buttonEnabled: false,
                    breakpoint: {
                        width: 1000
                    },
                    position: "bottom-left"
                }
            },
        });

        view.when(function() {

            var featureLayer = webmap.layers.getItemAt(0);

            featureLayer.labelingInfo = [{
                labelExpression: "[title]",
                labelExpressionInfo: {
                    "expression": "$feature[\"title\"]"
                },
                labelPlacement: "always-horizontal",
                symbol: {
                    type: "text",  
                    color: [ 255,255,255,0.85 ],
                    font: {
                        size: 16,
                        weight: "bold",
                        family: "Arial Unicode MS"
                    },
                    haloColor: [255, 255, 255, 255],
                    haloSize: 0.75,
                }
            }];
          
            var legend = new Legend({
                view: view,
                layerInfos: [{
                    layer: featureLayer,
                    title: "Actor type"
                }]
            });

            view.ui.add(legend, "bottom-right");

        });


        view.when(function() {
            var layerList = new LayerList({
                view: view
            });

            view.ui.add(layerList, "top-right");
        });



        watchUtils.whenTrue(view, "stationary", function() {
            if (view.extent) {
                var info = `the view extent changed: \n
                    xmin = ${view.extent.xmin.toFixed(2)} xmax = ${view.extent.xmax.toFixed(2)} \n
                    ymin = ${view.extent.ymin.toFixed(2)} ymax = ${view.extent.ymax.toFixed(2)}`;

                console.log(info);
            }
        });
    });
    </script>
</head>

<body>
    <div id="viewDiv"></div>
    <div>
        <p>Visible graphics:</p>
        <!-- TODO: Display visible graphics-->
    </div>
</body>

</html>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I have been trying different things:

1) Using queryGraphics as I found at the GraphicsLayerView | API Reference | ArcGIS API for JavaScript 4.6 

l = view.allLayerViews.getItemAt(1);
l.queryGraphics().then(function(results){
   console.log("results=",results);  
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But the "then" promise was not resolved, maybe because I didn't added in the right place.

2) So I tried using queryFeatures, similar al Robert Scheitlin, GISP‌ suggested in Capture attribute values from a WebMap without click event directly on the console:

view.allLayerViews.getItemAt(1).queryFeatures(query).always(function(results){
   console.info(results);
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But this time I used "always" and I got an:

{
  name: "FeatureLayerView2D", 
  message: "Not ready to execute query", 
  details: undefined
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

3) I didn't know what "Not ready to execute query" means so I keep trying this time using the view event layerview-create as Ken Buja‌ mentioned atCannot access items array property of MapView.allLayerViews (ArcJS API 4.4) :

view.on("layerview-create", function(event) {
  var query = new Query();
  query.where = '1=1';
  query.outSpatialReference = view.spatialReference;
  query.outFields = ["*"];

  console.log("event.layer.title=",event.layer.title)
  event.layerView.queryFeatures(query).always(function(results){
    console.info(results);
  });  
});
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But I got the same error.

4) Next time I tried a different way, using layerView.featuresView.graphics as Thomas Solow‌ mentioned at Design Question - Feature Layer Queries on Client-Side? 

l = view.allLayerViews.getItemAt(1);
l.layer.title // return "Startups"
l.featuresView.numFeatures // return 374
l.featuresView.graphics // return undefined‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And I don't understand why graphics are undefined this time... ^_^. I also tried using queryFeatures on l.featuresView but the method didn't exist.

5) Desperately I also tried using the Accessor:

l = view.allLayerViews.getItemAt(1)
l.watch("updating", function (newValue, oldValue, propertyName, target) {
  target.queryFeatures().then(function (results) {
      console.log("results", results);
  })
  console.log("graphics=", target.featuresView.graphics")
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But nothing ;(

I have spent several hours reading, trying different things and searching in the answered questions, but I'm running out of ideas. Please, could you help me solve this problem? I guess it should be quite easy to do but I'm not able to make it work.

Thanks in advance.

1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Raul,

   Here is your code updated to work best:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <title>Query graphics on webmap</title>

    <link rel="stylesheet" href="https://js.arcgis.com/4.7/esri/css/main.css">
    <script>
    var dojoConfig = {
        has: {
            "esri-featurelayer-webgl": 1
        }
    };
    </script>
    <script src="https://js.arcgis.com/4.7/"></script>

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

    <script>
      require([
          "esri/WebMap",
          "esri/views/MapView",
          "esri/widgets/LayerList",
          "esri/widgets/Legend",
          "esri/geometry/geometryEngine",
          "esri/core/watchUtils",
          "esri/tasks/support/Query",
          "dojo/domReady!"
      ],
      function(
          WebMap,
          MapView,
          LayerList,
          Legend,
          geometryEngine,
          watchUtils,
          Query
      ) {

        webmap = new WebMap({
          portalItem: { 
            id: "19d21df5e87f4f5a9aa5b8d678b174d6"
          }
        });

        view = new MapView({
          container: "viewDiv",
          map: webmap,
          popup: {
            dockEnabled: true,
            dockOptions: {
              buttonEnabled: false,
              breakpoint: {
                width: 1000
              },
              position: "bottom-left"
            }
          }
        });

        function graphicsInView(lyrView) {
          var query = new Query();
          query.geometry = view.extent;
          query.spatialRelationship = "intersects";
          lyrView.queryFeatures(query).then(function(results){
            console.log(`Graphics found: ${results.length}`);
          });
        }
        
        view.when(function() {
          var featureLayer = webmap.layers.getItemAt(0);
          
          view.whenLayerView(featureLayer).then(function(lyrView){
            watchUtils.whenFalseOnce(lyrView, "updating", function() {
              graphicsInView(lyrView);
            });
            watchUtils.whenTrue(view, "stationary", function() {
              graphicsInView(lyrView);
            });
          });

          featureLayer.labelingInfo = [{
            labelExpression: "[title]",
            labelExpressionInfo: {
              "expression": "$feature[\"title\"]"
            },
            labelPlacement: "always-horizontal",
            symbol: {
              type: "text",  
              color: [ 255,255,255,0.85 ],
              font: {
                size: 16,
                weight: "bold",
                family: "Arial Unicode MS"
              },
              haloColor: [255, 255, 255, 255],
              haloSize: 0.75,
            }
          }];

          var legend = new Legend({
            view: view,
            layerInfos: [{
              layer: featureLayer,
              title: "Actor type"
            }]
          });

          //view.ui.add(legend, "bottom-right");

          var layerList = new LayerList({
            view: view
          });

          //view.ui.add(layerList, "top-right");
        });
    });
    </script>
</head>

<body>
    <div id="viewDiv"></div>
    <div>
        <p>Visible graphics:</p>
        <!-- TODO: Display visible graphics-->
    </div>
</body>

View solution in original post

8 Replies
ReneRubalcava
Frequent Contributor

You need to wait for LayerView updating to be false, I think even in the case of the GraphicsLayerView.

Check this sample.

LayerView - Solution 

RobertScheitlin__GISP
MVP Emeritus

Raul,

EDIT: this seems to be an issue in 4.6 when using the webGL script portion. If you remove that the this sample and your code will working in 4.6. It has been fix in 4.7 though.

  It looks like 4.6 is real buggy when doing the client-side query. I agree with Rene you need to wait for the featureLayerView to not be updating but even then in 4.6 there are errors. Here is a sample using 4.7 that works fine but if you switch it to 4.6 you see you get the not ready to execute error.

<!DOCTYPE html>
<html>

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

  <link rel="stylesheet" href="https://js.arcgis.com/4.7/esri/css/main.css">
  <script>
    var dojoConfig = {
      has: {
        "esri-featurelayer-webgl": 1
      }
    };
  </script>
  <script src="https://js.arcgis.com/4.7/"></script>

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

  <script>
  var featLyrView;
    require([
        "esri/WebMap",
        "esri/views/MapView",
        "esri/widgets/LayerList",
        "esri/widgets/Legend",
        "esri/geometry/geometryEngine",
        "esri/core/watchUtils",
        "esri/tasks/support/Query",
        "dojo/domReady!"
      ],
      function(
        WebMap,
        MapView,
        LayerList,
        Legend,
        geometryEngine,
        watchUtils,
        Query
      ) {

        webmap = new WebMap({
          portalItem: {
            id: "19d21df5e87f4f5a9aa5b8d678b174d6"
          }
        });
        view = new MapView({
          container: "viewDiv",
          map: webmap,
          popup: {
            dockEnabled: true,
            dockOptions: {
              buttonEnabled: false,
              breakpoint: {
                width: 1000
              },
              position: "bottom-left"
            }
          },
        });

        view.when(function() {
          var featureLayer = webmap.layers.getItemAt(0);

          var query = new Query();
          query.geometry = view.extent;
          query.spatialRelationship = "intersects";

          view.whenLayerView(featureLayer).then(function(lyrView){
            featLyrView = lyrView
            lyrView.watch("updating", function (val) {
              if (!val){
                lyrView.queryFeatures(query).then(function(results){
                  console.log(results);  // prints the array of client-side graphics to the console
                });
              }
            });
          });

          featureLayer.labelingInfo = [{
            labelExpression: "[title]",
            labelExpressionInfo: {
              "expression": "$feature[\"title\"]"
            },
            labelPlacement: "always-horizontal",
            symbol: {
              type: "text",
              color: [255, 255, 255, 0.85],
              font: {
                size: 16,
                weight: "bold",
                family: "Arial Unicode MS"
              },
              haloColor: [255, 255, 255, 255],
              haloSize: 0.75,
            }
          }];

          var legend = new Legend({
            view: view,
            layerInfos: [{
              layer: featureLayer,
              title: "Actor type"
            }]
          });

          view.ui.add(legend, "bottom-right");
        });

        view.when(function() {
          var layerList = new LayerList({
            view: view
          });
          view.ui.add(layerList, "top-right");
        });

        watchUtils.whenTrue(view, "stationary", function() {
          if(featLyrView){
            featLyrView.queryFeatureCount().then(function (results) {
              console.log("results", results);
            }).otherwise(function(error){
              console.info(error);
            });
          }
        });
      });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
  <div>
    <p>Visible graphics:</p>
    <!-- TODO: Display visible graphics-->
  </div>
</body>

</html>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Raul_Jimenez
Esri Contributor

Thanks Robert Scheitlin, GISP & Rene Rubalcava!! Now it works , I'll share the app as soon as I have something to show ;-***

Raul_Jimenez
Esri Contributor

I have found an strange behaviour, as you can see here:

The layerView is not updating when new features are showing up and the query doesn't seem to be displaying the number of features in the screen, right?

This is the source code: https://jsbin.com/fuworis/11/edit?html,output 

Any ideas?:Rene Rubalcava‌ / Robert Scheitlin, GISP

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Raul,

   You have to update the query geometry each time:

view.whenLayerView(featureLayer).then(function(lyrView){
                featLyrView = lyrView
                lyrView.watch("updating", function (val) {
                    console.log("Updating layer view")
                    if (!val){
                      query.geometry = view.extent;
                      query.spatialRelationship = "intersects";
                        lyrView.queryFeatures(query).then(function(results){
                            console.log(`Graphics found: ${results.length}`);
                        });
                    }
                });
            });
Raul_Jimenez
Esri Contributor

hehe... ^_^'', you are right, (sorry, for the dummy question) -> https://jsbin.com/fuworis/13/edit?html,output 

What about the ViewLayer, it should be updated when a new feature shows up on the screen, right?

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Raul,

   Here is your code updated to work best:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <title>Query graphics on webmap</title>

    <link rel="stylesheet" href="https://js.arcgis.com/4.7/esri/css/main.css">
    <script>
    var dojoConfig = {
        has: {
            "esri-featurelayer-webgl": 1
        }
    };
    </script>
    <script src="https://js.arcgis.com/4.7/"></script>

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

    <script>
      require([
          "esri/WebMap",
          "esri/views/MapView",
          "esri/widgets/LayerList",
          "esri/widgets/Legend",
          "esri/geometry/geometryEngine",
          "esri/core/watchUtils",
          "esri/tasks/support/Query",
          "dojo/domReady!"
      ],
      function(
          WebMap,
          MapView,
          LayerList,
          Legend,
          geometryEngine,
          watchUtils,
          Query
      ) {

        webmap = new WebMap({
          portalItem: { 
            id: "19d21df5e87f4f5a9aa5b8d678b174d6"
          }
        });

        view = new MapView({
          container: "viewDiv",
          map: webmap,
          popup: {
            dockEnabled: true,
            dockOptions: {
              buttonEnabled: false,
              breakpoint: {
                width: 1000
              },
              position: "bottom-left"
            }
          }
        });

        function graphicsInView(lyrView) {
          var query = new Query();
          query.geometry = view.extent;
          query.spatialRelationship = "intersects";
          lyrView.queryFeatures(query).then(function(results){
            console.log(`Graphics found: ${results.length}`);
          });
        }
        
        view.when(function() {
          var featureLayer = webmap.layers.getItemAt(0);
          
          view.whenLayerView(featureLayer).then(function(lyrView){
            watchUtils.whenFalseOnce(lyrView, "updating", function() {
              graphicsInView(lyrView);
            });
            watchUtils.whenTrue(view, "stationary", function() {
              graphicsInView(lyrView);
            });
          });

          featureLayer.labelingInfo = [{
            labelExpression: "[title]",
            labelExpressionInfo: {
              "expression": "$feature[\"title\"]"
            },
            labelPlacement: "always-horizontal",
            symbol: {
              type: "text",  
              color: [ 255,255,255,0.85 ],
              font: {
                size: 16,
                weight: "bold",
                family: "Arial Unicode MS"
              },
              haloColor: [255, 255, 255, 255],
              haloSize: 0.75,
            }
          }];

          var legend = new Legend({
            view: view,
            layerInfos: [{
              layer: featureLayer,
              title: "Actor type"
            }]
          });

          //view.ui.add(legend, "bottom-right");

          var layerList = new LayerList({
            view: view
          });

          //view.ui.add(layerList, "top-right");
        });
    });
    </script>
</head>

<body>
    <div id="viewDiv"></div>
    <div>
        <p>Visible graphics:</p>
        <!-- TODO: Display visible graphics-->
    </div>
</body>
Raul_Jimenez
Esri Contributor

This is exactly the behaviour I was expecting, great! Robert Scheitlin, GISP‌, thank you very very much.

This is the code working -> https://jsbin.com/fuworis/edit?html,output 

0 Kudos