Select to view content in your preferred language

Query multiple layers on search

491
2
11-15-2024 09:27 AM
ToddFagin
Frequent Contributor

Several years ago, I (with help from this fine community) developed a simple app that, when a user searched for a location (address, XY, etc.) using the search widget, a featurelayer was also queried and a pop-up would provide information from that feature layer. Example: an individual would enter an address and the search widget would query a state house district layer, resulting in a pop-up with info. about the district in which the address occurs (code snippet below to show what was done).

 

 

 searchWidget.on('select-result', function(evt){
        let query = hsb.createQuery();
        query.geometry = evt.result.feature.geometry;
        query.spatialRelationship = 'within';
        query.outFields = ['*'];
        query.returnGeometry = true;
        query.outSpatialReference = view.spatialReference;
        query.maxAllowableOffset = 0;
        hsb.queryFeatures(query).then(function(result){
          view.popup.open({
            features: result.features,
            location: result.features[0].geometry.centroid
          });
        });
      });

 

 

We now have a need to do this for taxable jurisdictions. However, a single location can fall within multiple taxable jurisdictions (e.g. county, municipality, etc.), necessitating querying multiple layers and, ideally, returning a single pop-up.

The quickest, easiest solution would be to union the various layers and then query against it, but these data are dynamic, so being able to query each layer individually and returning the results at once is a better solution.

Anyone have any examples of something comparable I could use as a potential guide?

0 Kudos
2 Replies
JamesIng
Frequent Contributor

I would approach it in two steps - querying multiple layers, and then combining those results to display in a popup.

This is a rough crude example that I quick hacked up from the popup-domnode sample found here:
https://developers.arcgis.com/javascript/latest/sample-code/sandbox/?sample=popup-domnode

I've just got a sceneView, and added two sample layers onto the map for it.

On click I get the click location, buffer simply so I can get more results, and then use that bufferedPoint geometry to query the layers I added.

Once I have the results of the query I pass it to a function that generates some simple HTML that will be set as the contents of the Popup.

It's a pretty quick and dirty example, but illustrates the two key steps, querying for features, and then passing it onto the popup to display.

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Popup with DOM node | Sample | ArcGIS Maps SDK for JavaScript 4.31</title>

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

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

      .mapView {
        height: 400px;
        border: 1px solid #a8a8a8;
      }

      .esri-popup.esri-widget {
        max-height: 100%;
      }

      .esri-view-width-xlarge .esri-popup__main-container {
        width: 580px;
      }

      .esri-view-height-less-than-medium .esri-popup__main-container {
        max-height: 500px;
      }
    </style>

    <script>
      require(["esri/layers/FeatureLayer", "esri/Map", "esri/WebScene", "esri/views/MapView", "esri/views/SceneView", "esri/geometry/geometryEngine"], (
        FeatureLayer,
        Map,
        WebScene,
        MapView,
        SceneView,
        geometryEngine
      ) => {
        // Create the map from the given web scene id
        const map = new WebScene({
          portalItem: {
            id: "5a392557cffb485f8fe004e668e9edc0"
          }
        });
        
          const layer = new FeatureLayer({
            url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/US_National_Parks_Annual_Visitation/FeatureServer/0",
            outFields: ["*"]
          });
          
          const crimeLayer = new FeatureLayer({
            url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/ArcGIS/rest/services/SF%20Crimes%20by%20Block%20Group/FeatureServer/0",
            outFields: ["*"]
          }); 
          
          map.addMany([layer, crimeLayer]);


        // Create the SceneView
        const sceneView = new SceneView({
          map: map,
          container: "sceneViewDiv",
          center: [-97.75188, 37.23308],
            zoom: 5,
          popup: {
            actions: [],
            dockEnabled: true,
            dockOptions: {
              buttonEnabled: true,
              breakpoint: false
            }
          }
        });

        // Listen for when the scene view is ready
        sceneView.when(() => {
          // It's necessary to overwrite the default click for the popup
          // behavior in order to display your own popup
          sceneView.popupEnabled = false;
          sceneView.on("click", async (event) => {
            
              // Create lat/lon vars to display in popup title
              const lat = Math.round(event.mapPoint.latitude * 1000) / 1000;
              const lon = Math.round(event.mapPoint.longitude * 1000) / 1000;
              
              const point = sceneView.toMap(event);
              //Create a buffered point for querying features
              const bufferedPoint = geometryEngine.geodesicBuffer(point, 100, "kilometers");

    
              //Query each layer we're interested in
              const firstQuery =  layer.queryFeatures({
              geometry: bufferedPoint,
              outFields: ["*"]
            });

            const secondQuery = crimeLayer.queryFeatures({
              geometry: bufferedPoint,
              outFields: ["*"]
            });
              
            //Wait for the results of the queries
  
            const results = await Promise.all([firstQuery, secondQuery])
   
   
            //Pass those results to a popup via a function called 'setContentInfo' with the results
              sceneView.openPopup({
                // Set the popup's title to the coordinates of the location
                title: "Map view coordinates: [" + lon + ", " + lat + "]",
                location: event.mapPoint, // Set the location of the popup to the clicked location
                content: setContentInfo(results)
              });

          });

          function setContentInfo(results) {
            
            //Create an HTML element to display our results

            const popupDiv = document.createElement("div");
            popupDiv.classList.add("mapView");
            
            popupDiv.innerHTML = `Results count of first query = ${results[0].features.length},
            Results count of second query = ${results[1].features.length}`

            
            // Return a dom node
            return popupDiv;
          }
        });
      });
    </script>
  </head>

  <body>
    <div id="sceneViewDiv"></div>
  </body>
</html>




James from www.landkind.com
ToddFagin
Frequent Contributor

Excellent. I will give it a shot and see how it works on my end.

Greatly appreciated.

Todd

0 Kudos