What kind of query does queryFeatures support?

887
11
Jump to solution
12-26-2018 02:59 PM
DavidSchontzler
New Contributor III

I am attempting to query some FeatureLayers, but I cannot seem to construct a query that works.

If you click on the map in the following demo the promise returns an error: ArcGIS: queryFeatures - Unsupported query error 

  1. message: "Unsupported query"
  2. name: "esri.layers.graphics.QueryEngine"

I've tried a number of searches with different geometry types (extent, circle, geodesic buffer). I've also tried center and distance, but they are all unsupported.

Question: how can I find out what kinds of queries FeatureLayers support? I have not come across docs that have helped in that regard. Besides my sample FeatureLayer there are other FeatureLayers I will be querying (e.g. remote ones) and I would like to figure out what I can and cannot send in a query.

FYI, here is the code:

    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/geometry/geometryEngine",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/tasks/support/Query",
      "esri/widgets/CoordinateConversion",
      "esri/geometry/support/webMercatorUtils",
      "esri/geometry/Point",
      "dojo/domReady!"
    ], function(Map, MapView, FeatureLayer, geometryEngine, Graphic, GraphicsLayer, Query, CC, wMU, Point) {

      map = new Map({
        basemap: "topo-vector"
      });
      
      view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.739851, 34.090456],
        zoom: 14
      });
      
      view.ui.add(new CC({ view: view }), 'bottom-right');
      
      featureLayer = new FeatureLayer({
        id: 'dots',
        title: 'dots on a map',
        geometryType: 'point',
        objectIdField: 'id',
        spatialReference: { wkid: 4326 },
        source: [],
        fields: [
          {
            name: 'id',
            alias: 'ID',
            type: 'oid',
          },
          {
            name: 'mapObject',
            alias: 'mapObject',
            type: 'object'
          }
        ]
      });
      
      map.layers.add(featureLayer);
      
      var circleGraphic;
      
      points = [];
      
      [
        '-118.742555, 34.095076',
        '-118.747447, 34.094010',
        '-118.749593, 34.090811',
        '-118.734658, 34.088039',
        '-118.736032, 34.091735',
        '-118.743070, 34.091096'
      ].forEach(pair => {
        var ll = pair.split(', ');
        addPoint(new Point({
          type: 'point',
          spatialReference: { wkid: 4326 },
          x: ll[0],
          y: ll[1]
        }));
      });
      
      view.on('click', function (e) {
        var centerPt = e.mapPoint;
        if (e.native.ctrlKey) {
          // add more points to the map
          addPoint(centerPt);
        } else {
          // search in the clicked area for points
          var geom = geometryEngine.geodesicBuffer(centerPt, 400, 'meters');
          if (circleGraphic) view.graphics.remove(circleGraphic);
          circleGraphic = new Graphic({
            geometry: geom,
            symbol: {
              type: 'simple-fill',
              color: [140, 140, 222, 0.5]
            }
          });
          view.graphics.add(circleGraphic);
          
          query = featureLayer.createQuery();
          query.geometry = geom;
          query.spatialRelationship = 'contains';
          
          featureLayer.queryFeatures(query)
            .then(result => {
              console.log('This area contains', inPts.length, 'points');
            }).catch(error => {
              console.error(error);
            });
        }
      });
      
      function addPoint(geo) {
        if (geo.spatialReference.isGeographic) {
          geo = wMU.geographicToWebMercator(geo);
          console.log('converting geography');
        }
        var pt = new Graphic({
          geometry: geo,
          symbol: {
            type: 'simple-marker',
            style: 'circle',
            color: 'black',
            size: '8px'
          }
        });
        // console.log('adding point at:', pt.geometry.x, pt.geometry.y);
        view.graphics.add(pt);
        points.push(pt);
      }
    });
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

David,

   The issue you were having was you were adding the points to the view graphics and never the FeatureLayer, and several other issues. Here is the corrected code:

<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS DevLabs: JavaScript Starter App</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

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

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/geometry/geometryEngine",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/tasks/support/Query",
      "esri/widgets/CoordinateConversion",
      "esri/geometry/support/webMercatorUtils",
      "esri/geometry/Point",
      "dojo/domReady!"
    ], function(Map, MapView, FeatureLayer, geometryEngine, Graphic, GraphicsLayer, Query, CC, wMU, Point) {

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

      view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.739851, 34.090456],
        zoom: 14
      });

      view.ui.add(new CC({
        view: view
      }), 'bottom-right');

      featureLayer = new FeatureLayer({
        id: 'dots',
        title: 'dots on a map',
        geometryType: 'point',
        objectIdField: 'id',
        spatialReference: {
          wkid: 4326
        },
        source: [],
        fields: [{
            name: 'id',
            alias: 'ID',
            type: 'oid',
          },
          {
            name: 'origin',
            alias: 'Origin',
            type: 'string'
          }
        ],
        renderer: {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: {
            type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
            size: 6,
            color: "black",
            outline: { // autocasts as new SimpleLineSymbol()
              width: 0.5,
              color: "white"
            }
          }
        }
      });

      map.layers.add(featureLayer);

      var circleGraphic;

      points = [];

      [
        '-118.742555, 34.095076',
        '-118.747447, 34.094010',
        '-118.749593, 34.090811',
        '-118.734658, 34.088039',
        '-118.736032, 34.091735',
        '-118.743070, 34.091096'
      ].forEach(pair => {
        var ll = pair.split(', ');
        addPoint(new Point({
          type: 'point',
          spatialReference: {
            wkid: 4326
          },
          x: ll[0],
          y: ll[1]
        }), "original");
      });

      view.on('click', function(e) {
        var centerPt = e.mapPoint;
        if (e.native.ctrlKey) {
          // add more points to the map
          addPoint(centerPt, "user_added");
        } else {
          // search in the clicked area for points
          var geom = geometryEngine.geodesicBuffer(centerPt, 400, 'meters');
          if (circleGraphic) view.graphics.remove(circleGraphic);
          circleGraphic = new Graphic({
            geometry: geom,
            symbol: {
              type: 'simple-fill',
              color: [140, 140, 222, 0.5]
            }
          });
          view.graphics.add(circleGraphic);

          query = featureLayer.createQuery();
          query.geometry = wMU.webMercatorToGeographic(geom);
          query.spatialRelationship = "intersects"; // this is the default
          query.returnGeometry = true;
          query.outFields = ["*"];
          query.outSpatialReference = view.spatialReference;

          featureLayer.queryFeatures(query)
            .then(result => {
              console.info(result);
              console.log('This area contains', result.features.length, 'points');
            }).catch(error => {
              console.error(error);
            });
        }
      });

      function addPoint(geo, source) {
        if (geo.spatialReference.isWebMercator) {
          geo = wMU.webMercatorToGeographic(geo);
          console.info("Converting to Geographic");
        }
        var pt = new Graphic({
          geometry: geo,
          attributes: {
            origin: source
          }
        });
        points.push(pt);
        featureLayer.applyEdits({
          addFeatures: [pt]
        })
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

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

View solution in original post

11 Replies
DavidSchontzler
New Contributor III

I attempted to use FeatureLayerView.queryFeatures(), but the result is the same:

ArcGIS: queryFeatures - Unsupported query error - FeatureLayerView 

A partial response to my original question is that layer.capabilities.query should tell you what kind of queries you can make. I my case, featureLayer.capabilities.query.supportsExtent = true, but the error still persists when I use FeatureLayerView and pass an Extent for the geometry.

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

David,

   The issue you were having was you were adding the points to the view graphics and never the FeatureLayer, and several other issues. Here is the corrected code:

<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS DevLabs: JavaScript Starter App</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

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

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/geometry/geometryEngine",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/tasks/support/Query",
      "esri/widgets/CoordinateConversion",
      "esri/geometry/support/webMercatorUtils",
      "esri/geometry/Point",
      "dojo/domReady!"
    ], function(Map, MapView, FeatureLayer, geometryEngine, Graphic, GraphicsLayer, Query, CC, wMU, Point) {

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

      view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.739851, 34.090456],
        zoom: 14
      });

      view.ui.add(new CC({
        view: view
      }), 'bottom-right');

      featureLayer = new FeatureLayer({
        id: 'dots',
        title: 'dots on a map',
        geometryType: 'point',
        objectIdField: 'id',
        spatialReference: {
          wkid: 4326
        },
        source: [],
        fields: [{
            name: 'id',
            alias: 'ID',
            type: 'oid',
          },
          {
            name: 'origin',
            alias: 'Origin',
            type: 'string'
          }
        ],
        renderer: {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: {
            type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
            size: 6,
            color: "black",
            outline: { // autocasts as new SimpleLineSymbol()
              width: 0.5,
              color: "white"
            }
          }
        }
      });

      map.layers.add(featureLayer);

      var circleGraphic;

      points = [];

      [
        '-118.742555, 34.095076',
        '-118.747447, 34.094010',
        '-118.749593, 34.090811',
        '-118.734658, 34.088039',
        '-118.736032, 34.091735',
        '-118.743070, 34.091096'
      ].forEach(pair => {
        var ll = pair.split(', ');
        addPoint(new Point({
          type: 'point',
          spatialReference: {
            wkid: 4326
          },
          x: ll[0],
          y: ll[1]
        }), "original");
      });

      view.on('click', function(e) {
        var centerPt = e.mapPoint;
        if (e.native.ctrlKey) {
          // add more points to the map
          addPoint(centerPt, "user_added");
        } else {
          // search in the clicked area for points
          var geom = geometryEngine.geodesicBuffer(centerPt, 400, 'meters');
          if (circleGraphic) view.graphics.remove(circleGraphic);
          circleGraphic = new Graphic({
            geometry: geom,
            symbol: {
              type: 'simple-fill',
              color: [140, 140, 222, 0.5]
            }
          });
          view.graphics.add(circleGraphic);

          query = featureLayer.createQuery();
          query.geometry = wMU.webMercatorToGeographic(geom);
          query.spatialRelationship = "intersects"; // this is the default
          query.returnGeometry = true;
          query.outFields = ["*"];
          query.outSpatialReference = view.spatialReference;

          featureLayer.queryFeatures(query)
            .then(result => {
              console.info(result);
              console.log('This area contains', result.features.length, 'points');
            }).catch(error => {
              console.error(error);
            });
        }
      });

      function addPoint(geo, source) {
        if (geo.spatialReference.isWebMercator) {
          geo = wMU.webMercatorToGeographic(geo);
          console.info("Converting to Geographic");
        }
        var pt = new Graphic({
          geometry: geo,
          attributes: {
            origin: source
          }
        });
        points.push(pt);
        featureLayer.applyEdits({
          addFeatures: [pt]
        })
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
DavidSchontzler
New Contributor III

Thank you for the reply. That worked! I made the mistake of forking an old CodePen which was not adding the items to the feature layer.

My original issue was that the query was not supported. I assume that is because I did not convert the geometry to a geographic one.

Once the query was correct, I still would not receive any result if applyEdits is not used, correct? Should I be checking something like layer.capabilities.editing.supportsEditing before attempting to use applyEdits?

I received the following warning:

"[esri.layers.FeatureLayer]" "FeatureLayer.hasAttachments is deprecated. Use FeatureLayer.capabilities.data.supportsAttachment instead."

That is never called explicitly in the code. Is that something that Esri just has not updated internally?

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Once the query was correct, I still would not receive any result if applyEdits is not used, correct?

No. I was able to get results for the points in the buffer using this instead of applyEdits:

      function addPoint(geo, source) {
        if (geo.spatialReference.isWebMercator) {
          geo = wMU.webMercatorToGeographic(geo);
          console.info("Converting to Geographic");
        }
        var pt = new Graphic({
          geometry: geo,
          attributes: {
            origin: source
          }
        });
        points.push(pt);
        featureLayer.source = points;
      }

I just could not get the user added points to display unless I used applyEdits.

Should I be checking something like layer.capabilities.editing.supportsEditing before attempting to use applyEdits?

If you are creating the FeatureLayer client side using a collection of graphics then No that step is not needed.

I do not receive the 

"[esri.layers.FeatureLayer]" "FeatureLayer.hasAttachments is deprecated. Use FeatureLayer.capabilities.data.supportsAttachment instead."

 warning in my code?...

DavidSchontzler
New Contributor III

I'll experiment with applyEdits in my actual code to see when it's needed.

Is there some documentation on when and where geometry conversion is needed? e.g.

query.geometry = wMU.webMercatorToGeographic(geom)
0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

David,

   Any time you are comparing (doing a spatial query) the geometries have to be in the same spatial reference.

0 Kudos
DavidSchontzler
New Contributor III

Makes sense. In my particular app I am using wkid 102100. When I convert the plotted points to that spatial reference the query fails again. I am no longer converting the geodesic buffer because my featureLayer and the buffer are both using wkid 102100.

CodePen: ArcGIS: queryFeatures - Unsupported query error - 3 wkid 102100 

I also came across this: GitHub - Esri/terraformer-arcgis-parser. I don't know if that would be a help in the case where I'm using wkid 102100.

Thank you for all of your help.

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

David,

  There is no need for terraformer. Your code just need to be tweaked:

<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS DevLabs: JavaScript Starter App</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

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

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/geometry/geometryEngine",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/tasks/support/Query",
      "esri/widgets/CoordinateConversion",
      "esri/geometry/support/webMercatorUtils",
      "esri/geometry/Point",
      "dojo/domReady!"
    ], function(Map, MapView, FeatureLayer, geometryEngine, Graphic, GraphicsLayer, Query, CC, wMU, Point) {

      map = new Map({
        basemap: "streets"
      });

      view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.739851, 34.090456],
        zoom: 14
      });

      view.ui.add(new CC({
        view: view
      }), 'bottom-right');

      featureLayer = new FeatureLayer({
        id: 'dots',
        title: 'dots on a map',
        geometryType: 'point',
        objectIdField: 'id',
        spatialReference: {
          wkid: 102100
        },
        source: [],
        fields: [{
            name: 'id',
            alias: 'ID',
            type: 'oid',
          },
          {
            name: 'origin',
            alias: 'Origin',
            type: 'string'
          }
        ],
        renderer: {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: {
            type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
            size: 6,
            color: "black",
            outline: { // autocasts as new SimpleLineSymbol()
              width: 0.5,
              color: "white"
            }
          }
        }
      });

      map.layers.add(featureLayer);

      var circleGraphic;

      points = []; // for reference

      function addPoint(geo, source) {
        console.log('adding point', geo.x, geo.y, source);
        var pt = new Graphic({
          geometry: geo,
          attributes: {
            origin: source
          }
        });
        points.push(pt);
        //featureLayer.source.add(pt);
        //dojo.js:310 [esri.layers.graphics.sources.MemorySource] Source modifications will not propagate after layer has been loaded. Please use .applyEdits() instead
        featureLayer.applyEdits({
          addFeatures: [pt]
        })
      }

      var wkPts = [
        '-13217438.998, 4041250.727',
        '-13217610.981, 4041059.635',
        '-13217945.393, 4041050.080',
        '-13217878.511, 4041403.601',
        '-13218413.570, 4040572.348',
        '-13218002.721, 4040409.920'
      ];

      wkPts.forEach(pair => {
        var xy = pair.split(', ');
        addPoint(new Point({
          spatialReference: featureLayer.spatialReference,
          x: xy[0],
          y: xy[1]
        }), "original");
      });

      view.on('click', function(e) {
        var centerPt = e.mapPoint;
        if (e.native.ctrlKey) {
          // add more points to the map
          addPoint(centerPt, "user_added");
        } else {
          // search in the clicked area for points
          var geom = geometryEngine.geodesicBuffer(centerPt, 400, 'meters');
          if (circleGraphic) view.graphics.remove(circleGraphic);
          circleGraphic = new Graphic({
            geometry: geom,
            symbol: {
              type: 'simple-fill',
              color: [140, 140, 222, 0.5]
            }
          });
          view.graphics.add(circleGraphic);

          query = featureLayer.createQuery();
          query.geometry = geom;
          query.spatialRelationship = "intersects";
          query.returnGeometry = true;
          query.outFields = ["*"];
          query.outSpatialReference = view.spatialReference;

          featureLayer.queryFeatures(query)
            .then(result => {
              // console.info(result);
              console.log('This area contains', result.features.length, 'points');
            }).catch(error => {
              console.error(error);
            });
        }
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
DavidSchontzler
New Contributor III

Robert,

Thank you for the reply. We are currently using ArcGIS 4.8 and cannot upgrade to 4.10 yet, so I tried changing from 4.10 to 4.8 and it stopped working. Interestingly, applyEdits didn't add the points in 4.8, but source = points did work. But the query still returns an error. I wonder if there's a bug in 4.8?

CodePen sample of your suggestion in 4.8 using source = pointsArcGIS: queryFeatures - Unsupported query error - 4 wkid 102100 

Edit: A quick comparison of featureLayer.capabilities.query in 4.10 and 4.8 show that 4.8 only has supportExtent = true, whereas 4.10 has a more supported queries. I've edited the example to use an extent instead of a geodesic buffer, but the error persists. I've confirmed that both the query and the featureLayer have the same spatialReference.wkid as well.

0 Kudos