0

I am using the Bootleaf IAG framework.

I can not figure out how to get the bounding coordinates of a filtered layer.

I am modifying the bootleaf code to query points with a polygon layer. The Query Widget already allows users to draw a polygon, but I want to select a polygon from a layer hosted on my arcgis server. I modified the filter widget by removing the text field and allowing my users to select polygon layers and values from a dropdown menu. This works fine.

Now I need to take the result of the layer.setWhere(where, handleError); code and merry it with the query below. I need selectedPolygon to equal the result of layer.setWhere(where, handleError); and use the bounding coordinates in the .within section of the query.

I have tried a number of things, L.latLngBounds, getBounds(), and toGeoJSON().features[0].geometry.coordinates to name a few, but but I can not figure out how to pull out the bounds. What is the correct code?

    const query = L.esri.query({ url: pointInPolygonUrl })
        .token(pointInPolygonData.token)
        .within(selectedPolygon)
    query.run(function (error, data, response) {
        if (error) {
            console.log(error);
            return;
        }

6/8/2021 Edit (based on Seth Lutske's comment:

I did not provide a code sandbox for two reasons: 1 - bootleaf has a lot of files, 2 - all of my layers require secure sign in to arcgis. Hopefully I can provide enough information to get assistance without it.

  1. Is selectedPolygon changing the way I am expecting? Currently there is no variable called selectedPolygon because I can not figure out the correct way to format it. selectedPolygon is what I want to call the filter result layer.setWhere(where, handleError);. I set the polygon layer up to filter on the map as the value changes. I can verify it is filtering as expected.

  2. selectedPolygon format - This is where my problem lies. I can not seem to find the correct format based on how bootleaf layers are configured. I started with var selectedPolygon = layer.features.geometry.coordinates; and got a geometry undefined error. I proceeded to try every other code I could think of to get the bounds.

  3. Bounding coordinates may not be the proper terminology. I want to run a query to find all of the points within the filtered polygon. To achieve this, it is my understanding that I need to use the bounds of the filtered polygon in the within section of the query.

6/8/2021 Edit #2

This link may be most beneficial to show how the layer is constructed. I modified this code to remove the text input and add a dropdown, but the basic definition should be the same.

Line 1605 is function addFilter()

Line 1804 is function applyFilter()

Line 1927 is layer.setWhere(where, handleFilterError);

Photo 1: console.log("polygon layer", layer)

Photo 1

Photo 2: Expanded _layers

Photo 2

Photo 3: Expanded _rings (I did not find ToGetJSON, but I found ToGeoJSON in this section.

Photo 3

It looks like if I can get to _rings then I should be fine, but that is where my knowledge is lacking.

7
  • within can accept quite a few different kinds of geometry type arguments. 1 are you sure the selectedPolygon is changing according to your UI in the way you expect? 2 Is selectedPolygon in any of the accepted formats as described in the docs I linked? What format is it in? 3 You want to use the bounding coordinates of selectedPolygon, or the polygon itself, in the within query? !important: As always, providing a codesandbox demonstrating the issue will make helping you much easier. Commented Jun 7, 2021 at 23:45
  • @SethLutske I added additional information above. Thanks Commented Jun 8, 2021 at 14:24
  • Reading that format is pretty difficult. I'd say maybe post a picture of the console so I can make sense of it. Or tell me how layer gets defined. Is there a toGetJSON anywhere up the prototype chain on that (under the __proto__ property)? Commented Jun 8, 2021 at 14:39
  • @SethLutske I thought it might be hard to read. I remove that code and added additional information above. Commented Jun 8, 2021 at 16:51
  • Ok...interesting...a few things. That code is 3000 lines long...very unreadable. My recommendation is to modularize it for gods sakes! Second. looks like the layer you're printing is bootleaf.layers...what is that? I don't see any documentation about bootleaf's api. The rings you see are arcgis js api rings, which each represent a single polygon, usually in a multipolygon layer. Have you checked the __proto__ of layer? Does it not contain .toGeoJSON? Commented Jun 8, 2021 at 17:09

1 Answer 1

0

I don't know much about bootleaf, but here are some tips to get you started. Based on your question and comments, this will hopefully clear things up and instruct you on how to apply what you need in your scenario.

Hook UI to setWhere

When the user selects an option from the UI, you can call setWhere on the layer you're providing from the arcgis server. Let's say there's a polygon layer, in my example, called statesFeatureLayer, which is an L.esri.featureLayer

// Create polygon layer of states
const statesFeatureLayer = EL.featureLayer({
  url: "polygon_featurelayer_url_from_arcgis_server"
}).addTo(map);

And there's a point layer:

// Create points layer
const pointsFeatureLayer = EL.featureLayer({
  url: "points_featurelayer_url"
}).addTo(map);

Now there is some UI, which has to trigger setWhere to be called on this layer. So anywhere in the UI where you want to run this functionality of setting the filter on the layer, and then querying the other layer based on the results, we'll run a function call runQuery:

function runQuery(){
  statesFeatureLayer.setWhere(querystring, callback)
}

Run callback after setWhere fires

It sounds like you've already got this part figured out, and that your setWhere function is running properly. However, setWhere also takes an optional callback function as its second argument, which runs after the where has been set and the layer refreshed. Let's dig into that. In the callback, we're going to want to get all the features that are currently active on the map:

function runQuery(){
  statesFeatureLayer.setWhere(querystring, () => {
    statesFeatureLayer.eachActiveFeature(feature => {
      // do something with features
    })
  })
}

Run query to test points layer against active features of polygon layer

Within eachActiveFeature, we can run a query on the pointsFeatureLayer:

function runQuery(){
  statesFeatureLayer.setWhere(querystring, () => {
    statesFeatureLayer.eachActiveFeature(feature => {
      pointsFeatureLayer
        .query()
        .within(feature.toGeoJSON())
        .run((error, data) => {
          console.log(data);
        });
    })
  })
}

So now were are running a query which asks for any points in the pointsFeatureLayer that are in the geometry of each active feature of the statesFeatureLayer.

The downside of this is that we can't run a query against all the active features as a group. The within query method (along with most of the other query methods) can accept singular features, whether in the form of an L.Polygon, L.Polyline, or an L.GeoJSON. While I had tried creating an L.featureGroup and calling .toGeoJSON on that, within seems to require a GeoJSON that describes only a single shape. So if you have multiple features, you'll have to conglomerate them. For example, you may have some variable results = [] at the global scope level, then within the callback of run, you can push the results to results, which will give you all results in one variable. This may take some massaging in js to get it right.

Working Codesandbox

Here you have 2 UI elements which cause runQuery to run. Either the dropdown, or the checkbox. You'll see that on every UI change, setWhere is called with a querystring constructed from the UI (setWhere for a state, and setwhere for that state and california if the checkbox is checked). When setWhere is called, its callback then runs a query against the point layer just for the currently active features, and then returns whatever points from the pointlayer are within each of the active features.

1
  • Spot on mate. A modified version of the function runQuery() did the trick. I think it had to do with the use of eachActiveFeature(feature =>. Thank you very much! Commented Jun 9, 2021 at 14:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.