slutske22 / react-esri-leaflet

react components for esri-leaflet
https://codesandbox.io/s/github/slutske22/react-esri-leaflet/tree/master/examples/ts
MIT License
37 stars 5 forks source link

Question: how to use setWhere or equivalent with a FeatureLayer❓ #5

Closed rylincoln closed 3 years ago

rylincoln commented 3 years ago

Hi, I'm trying to give a UI to allow the user to modify the default where clause in a <FeatureLayer/> I have not yet figured out how to make the map rerender properly after the variables in the where clause change.

I'm passing in the year as a prop to this functional component - and that does update the where clause - but the layer disappears from the <MapContainer/> and I am getting a 400 error about the query not being able to execute. If I trigger a re-render of the entire <TheMap/> component then it works. Was reading code doc for the setWhere

 * Sets the new where option and refreshes the layer to reflect the new where filter. Accepts an optional
             * callback and function context.
             */
            setWhere(where: string, callback?: FeatureCallbackHandler, context?: any): this;
            /**

Here is a snippet of what I'm currently doing. I may simply not understand react and re-rendering flow.

const TheMap = ({ year }) => {
    return (
        <MapContainer
      id="mapId"
      style={mapstyle}
      zoom={zoom}
      center={{ lat: lat, lng: lon }}
    >
      <MapEvents />
      <LayersControl position="topleft" collapsed={false}>
        <LayersControl.Overlay name="Point Defects" checked>
          <FeatureLayer
            cacheLayers={false}
            where={`pr > 1 AND setup_id LIKE '${year}%'`}
            pointToLayer={function (features) {
              return L.marker(features.geometry.coordinates.reverse(), {
                icon: makeIcon(colors[features.properties.pr], 4, "black", 30),
              });
            }}
            url="{featureServiceURL}",
            eventHandlers={{
              loading: () => console.log("loading features"),
              load: () => console.log("featurelayer loaded"),
            }}
          />
        </LayersControl.Overlay>
      </LayersControl>
    </MapContainer>
    )
}
slutske22 commented 3 years ago

Interesting. This may be a bug with react-esri-leaflet, though its hard to say just from this code. It appears as though the FeatureLayer should call the underlying setWhere function when the year prop changes, and update the layer.

Are you able to create a reproducible example on a codesandbox, complete with a sensible featurelayer url and UI to adjust the where prop? If so, I can help debug this issue.

slutske22 commented 3 years ago

I don't think this is an issue with react-esri-leaflet. I forked the example repo and instituted some where logic. Check it out:

Working Codesandbox

There's a select menu on the bottom left. As you select options, it affects the state variable whereclause, which gets passed to the Map component as a prop, and then gets used in the where prop of a FeatureLayer. As you click the different counties, you'll see the FeatureLayer is filtering properly. Sorry the CSS for the markers is messed up, I have to fix that.

To be fair, I had a hell of a time getting the exact correct SQL string formatting to get this to work. I'd double check that your where clause is using perfect SQL, the feature attribute name, etc. I'd be happy to help debug if you make a sandbox, but I don't think this is an issue with react-esri-leaflet.

rylincoln commented 3 years ago

Yep your codesandbox works and is a good example of what I'm trying to do. The only thing I see obviously different is that I'm using pointToLayer and creating divIcons for the points

const makeIcon = (fill, strokeW, strokeC, r = 40) => {
  var iconSettings = {
    mapIconUrl: `<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 149 178"><circle cx="50" cy="50" r="${r}" stroke="${strokeC}" stroke-width="${strokeW}" fill="${fill}" /></svg>`
  };
  return L.divIcon({
    className: "leaflet-data-marker",
    html: L.Util.template(iconSettings.mapIconUrl, iconSettings), //.replace('#','%23'),
    iconAnchor: [12, 32],
    iconSize: [25, 30],
    popupAnchor: [0, -28]
  });
};
.....
<FeatureLayer
            cacheLayers={false}
            where={whereClause}
            pointToLayer={function (features) {
              return L.marker(features.geometry.coordinates.reverse(), {
                icon: makeIcon(colors[features.properties.pr], 4, "black", 30),
              });
            }}
            url={pointFeatureServiceURL}
            eventHandlers={{
              loading: () => console.log("loading features"),
              load: () => console.log("featurelayer loaded"),
            }}
          />

FYI - I tried to make a code sandbox but I kept getting L is undefined even though the leaflet dependency was included and I could drill down through the L namespace and get to the export for it... dunno.

Thanks for your help though.

slutske22 commented 3 years ago

I forked that sandbox and included your pointToLayer logic. Still seems to work:

Working Sandbox

When you look in your network tab, are new network requests being made when you trigger a where change? If not, that's a problem. If so, what is the network response? Is the request what you think it is?

I'm happy to help debug, as I love this stuff. See if you can fork that codesandbox, and use your featurelayer url and your where clauses in some UI that makes sense, and I can take a look. We can continue the discussion here, but I'm going to close the issue.

rylincoln commented 3 years ago

For some reason when I click my button to change the whereclause the request to the server is changing the URL parameter for format to geojson from json

Example:

I'm investigating if the server I'm taking to doesn't support the f=geojson URL param -- I'm no esri server guru 🤷🏽

But I think the above URL param difference is my root cause.

I am not sure why that network call URL param is changing - I do not have it defined anywhere.

Also sidenote; I'm working with secured services sorry for not providing complete code - but that is the reasoning.

slutske22 commented 3 years ago

I'm working with secured services sorry for not providing complete code - but that is the reasoning.

No worries, I figured as much.

So you're digging past react-esri-leaflet, ersi-leaflet, straight into the arcgis rest api for this. Take a look at the common parameters used in esri requests. The second item on that list is indeed f, which determines the format is returned in. Its definitely strange that the state change on the FeatureLayer causes a different f param to be used. The only esri-leaflet option I see that affects the format is isModern - search for it in the esri-leaflet featurelayer docs. Try playing with that to force the featurelayer to always get a json.

If that doesn't work (and I was in your shoes), I'd actually open up an issue on the esri-leaflet repo and see what they recommend about why the where option is having that sort of effect on the f param.

Let me know how this goes! I love esri, leaflet, and esri-leaflet and your issue helps me learn more about the whole ecosystem.

rylincoln commented 3 years ago

You nailed it! The isModern option set to false forces the esri-leaflet to request as json and not geojson. And now the feature layer updates as expected in my project. Thanks!

I'll open an issue with esri-leaflet to ask why setting the where option which I presume uses the setWhere option underneath this lib changes the f param to geojson.

No idea why the server I'm talking to at version 10.81 doesn't know about the f=geojson parm.

slutske22 commented 3 years ago

The esri-leaflet guys are great, but I would definitely consider creating the issue with a codesandbox replicating the issue, not using react-esri-leaflet, to make sure it is indeed not some wierd react thing, and isolate the problem properly.

Glad you like REL, happy codings.