PMTiles icon indicating copy to clipboard operation
PMTiles copied to clipboard

cannot set fetch options with maplibre protocol

Open eknowles opened this issue 1 year ago • 3 comments

When using a custom maplibre.addSource to use pmtiles, the PMTiles.getBytes method uses the global fetch method, this is done during the read metadata stage I think.

This means users can not alter the options such as setting credentials

https://github.com/protomaps/PMTiles/blob/main/js/index.ts#L379-L399

Once this has been read maplibre (i think) will then use the loaders.gl fetch methods which does allow the user to override this at a global level.

https://loaders.gl/docs/modules/core/api-reference/fetch-file

Could the pmtiles protocol also use this method so that users can fetch pmtiles using cors + credentials?

eknowles avatar May 24 '24 16:05 eknowles

You can initialize a FetchSource with CustomHeaders:

https://protomaps.github.io/PMTiles/typedoc/classes/FetchSource.html

bdon avatar May 25 '24 03:05 bdon

Thank you for the quick response, how would that work with addSource?

maplibre expects a string here both for tiles and url

eknowles avatar May 25 '24 11:05 eknowles

See this example: https://github.com/protomaps/PMTiles/blob/main/js/examples/maplibre.html#L26

You can then reference it in a style via the same Url and it will resolve to the same instance

new pmtiles.PMTiles(fetchSource)

bdon avatar May 26 '24 02:05 bdon

Would this be instead of using the built-in maplibre transformRequest? If pmtiles can use custom headers why noot just use the headers and create a fetch source internally using the existing headers?

reyemtm avatar Sep 10 '24 14:09 reyemtm

And also, if we use this method with the custom FetchSource, does that mean we need a custom PMTiles for each url we want to use, rather than simply adding headers to all pmtiles requests like we can do with transformRequest?

For my use case I am just adding a header from a url query param in the PMTiles class, but would rather not use a custom fork. If you can point me in the direction to using the existing headers that's sent along with the request that would be awesome.

reyemtm avatar Sep 10 '24 19:09 reyemtm

built-in maplibre transformRequest

transformRequest does not apply to custom protocols.

If pmtiles can use custom headers why noot just use the headers and create a fetch source internally using the existing headers?

There is no way to specify headers through MapLibre style URLs, you can only pass a string.

does that mean we need a custom PMTiles for each url we want to use

You need to instantiate a PMTiles javascript object and add it to the protocol for each URL, because there is no other way to specify it though the maplibre style object.

bdon avatar Sep 11 '24 01:09 bdon

using the existing headers that's sent along with the request

I don't know what this refers to?

bdon avatar Sep 11 '24 01:09 bdon

I figured out your code after reading it through a few times, so here it is for those looking for how to do this:

    const pmtilesProtocol = new Protocol();
    maplibregl.addProtocol("pmtiles", pmtilesProtocol.tile);
    const authHeaders = new Headers({ Authorization: `Bearer ${userToken}` });
    const URL = "https://urltotiles.com/data.pmtiles";
    pmtilesProtocol.add(new PMTiles(new FetchSource(URL, authHeaders)));

But with this method I have to do this with all the pmtile urls I want to add. In Maplibre you can add headers to all/any requests via transformRequest, and it would be much simpler to use this method. I think this would have to be defined in the Protocol class instance, as the custom headers are available there, but I'm not familiar enough with the code to know if those would get persisted throughout the code. If you don't want to support this that's fine, but could you something to the docs like PMTiles does not support the transformRequest option in relation to custom headers, use the built-in FetchSource instead.

const map = new maplibregl.Map({
    ...
     transformRequest: (url, resourceType) => {
        if (url.includes("pmtiles")) {
          return {
            url: url,
            headers: {
              Authorization: `Bearer ${userToken}`
            },
          };
        }
      }
  })

reyemtm avatar Sep 11 '24 02:09 reyemtm

now that I look at this closer we can add the custom headers from transformRequest like this inside the Protocol class:

    const headers = new Headers({ ...(params?.headers || {}) });
    const source = new FetchSource(pmtilesUrl, headers);
    instance = new PMTiles(source);

Is this something you would be willing to add or accept a PR?

reyemtm avatar Sep 11 '24 02:09 reyemtm

Updated the TypeDoc here:

https://pmtiles.io/typedoc/classes/FetchSource.html https://pmtiles.io/typedoc/classes/Protocol.html

But with this method I have to do this with all the pmtile urls I want to add.

Can you accomplish this using a loop or reusable function?

bdon avatar Sep 18 '24 08:09 bdon

Custom headers example: https://github.com/protomaps/PMTiles/blob/main/js/examples/maplibre_headers.html

bdon avatar Sep 22 '24 11:09 bdon