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

Geosearch Multiple Providers of same type ? #20

Open rylincoln opened 1 year ago

rylincoln commented 1 year ago

Perhaps (probably?) I'm doing something wrong but here is my scenario.

Using the EsriLeafletGeoSearch plugin, we configure providers by passing an object who's keys are parsed into the provider type that esri-leaflet-geocoder supports.

so example object looks something like this:

                mapServiceProvider: {
                        token: token,
                        url: "https://server/server/rest/services/folder/service/MapServer",
                        layers: [21],
                        searchFields: ["id"],
                        label: "Feature X",
                        searchMode: "strict",
                        bufferRadius: 10,
                        maxResults: 20,
                        formatSuggestion: function (feature) {
                            return `${feature.properties.id}` || null;
                        },
                    },
              // mapServiceProvider: {
                    //     token: token,
                    //     url: "https://server/server/rest/services/folder/service/MapServer",
                    //     layers: [3],
                    //     searchFields: ["name", "id"],
                    //     label: "Feature Y",
                    //     searchMode: "startsWith",
                    //     bufferRadius: 10,
                    //     maxResults: 20,
                    //     formatSuggestion: function (feature) {
                    //         return `${feature.properties.id} ${feature.properties.name}` || null;
                    //     },
                    // },

Since the object is then parsed using Object.keys(object) to tell ELG which provider type we want being one of

It appears to me that one cannot configure 2 or more of the same type of provider this way because you'd have to create an object with duplicate keys and I think the last one in wins.

In the ELG documentation here - it looks like it can support multiple providers of the same type maybe? https://developers.arcgis.com/esri-leaflet/api-reference/controls/geosearch/#providers

But it looks like it would require us passing in an array of objects so we're not relying on the keys of an object for provider type and instead we use a key/value pair that defines the provider type.

So would that be changing things around here? https://github.com/slutske22/react-esri-leaflet/blob/fc99d11583f34510ede87fc9eca56e560bd5a38b/src-plugins/EsriLeafletGeoSearch.tsx#L32

Perhaps it would accept something like this example below to configure multiple of the same type of providers?

[
    {
        "type": "mapServiceProvider",
        "options": {
            "token": token,
            "url": "https://server/server/rest/services/folder/service/MapServer",
            "layers": [21],
            "searchFields": ["id"],
            "label": "Feature X",
            "searchMode": "strict",
            "bufferRadius": 10,
            "maxResults": 20,
            "formatSuggestion": function (feature) {
                return `${feature.properties.id}` || null;
            },
        }
    },
    {
        "type": "mapServiceProvider",
        "options": {
            "token": token,
            "url": "https://server/server/rest/services/folder/service/MapServer",
            "layers": [3],
            "searchFields": ["name", "id"],
            "label": "Feature X",
            "searchMode": "strict",
            "bufferRadius": 10,
            "maxResults": 20,
            "formatSuggestion": function (feature) {
                return `${feature.properties.id} ${feature.properties.name}` || null;
            },
        }
    },
]

Thanks for listening.

slutske22 commented 1 year ago

Interesting use case, I hadn't thought of that. Is the idea to have 2 different mapServiceProviders, with different urls? It looks like you are trying to use the same url, but with different searchFields and layers, which seems a bit redundant.

Anyway yes, this is a use case I hadn't considered and your suggestion to turn the prop into an array is a good one. I'll try to pubish a change when I get a chance.

rylincoln commented 1 year ago

Yes, I'm using the same url, but different layers[] to search, more for a separate of concerns to simplify the formatSuggestions as the fields to search will be different for each layer.

rylincoln commented 1 year ago

FYI - I setup a component to solve for me now that I understand it more.

import { useEffect } from 'react'
import { useMap } from 'react-leaflet'
import * as ELG from 'esri-leaflet-geocoder'
import useArcGISToken from "../hooks/useArcGISToken";

const EsriLeafletGeoCoder = () => {
    const [token, error] = useArcGISToken();
    if (error) {
        console.log("error", error);
    }
    const map = useMap()

    useEffect(
        () => {
            if (token) {
                const layer21 = ELG.mapServiceProvider({
                    url: 'https://server.com/server/rest/services/folder/servicename/MapServer',
                    token: token,
                    layers: [21],
                    searchFields: ["id"],
                    label: "Layer 21",
                    searchMode: "strict",
                    bufferRadius: 10,
                    maxResults: 10,
                    formatSuggestion: function (feature) {
                        return `${feature.properties.id}` || null;
                    },
                });

                const layer3 = ELG.mapServiceProvider({
                    url: 'https://server.com/server/rest/services/folder/servicename/MapServer',
                    token: token,
                    layers: [3],
                    searchFields: ["id","name"],
                    label: "Layer 3",
                    searchMode: "startWith",
                    bufferRadius: 10,
                    maxResults: 10,
                    formatSuggestion: function (feature) {
                        console.log(feature)
                        return `${feature?.properties?.id} | ${feature?.properties?.name}` || null;
                    },
                });

                const GeoSearch = new ELG.Geosearch({
                    useMapBounds: false,
                    collapseAfterResult: false,
                    expanded: true,
                    providers: [layer21, layer3]
                })

                GeoSearch.addTo(map)

                return () => {
                    GeoSearch.remove()
                }
            } else {
                return () => {
                    console.log("no token")
                }
            }

        }
        , [map, token])

    return null

}

export default EsriLeafletGeoCoder
slutske22 commented 1 year ago

That's one way to workaround, very nice.

Another may be just to grab a ref to the EsriLeafletGeosearch component and modify its options directly:

  const MyMap = () => {
    const [ref, setRef] = useState();

    useEffect(() => {
      if (ref){
        ref.options.providers = [provider1, provider2, etc];
      }
    }, [ref])

    return (
      <MapContainer>
        <EsriLeafletGeoSearch
          ref={ref}
          providers={{}}
          key={ARCGIS_API_KEY}
        />
      </MapContainer>
    );
  };

Not sure if you can modify those providers after the fact, its just another idea till I can patch this package.