Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept Promise as return type for transformRequest #1012

Merged
merged 1 commit into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ sure that sprite image loading works:
| `sourceOrLayers` | `string` \| `string`\[] | `undefined` | `source` key or an array of layer `id`s from the Mapbox Style object. When a `source` key is provided, all layers for the specified source will be included in the style function. When layer `id`s are provided, they must be from layers that use the same source. |
| `resolutions` | `number`\[] | `defaultResolutions` | Resolutions for mapping resolution to zoom level. |
| `spriteData` | `any` | `undefined` | Sprite data from the url specified in the Mapbox Style object's `sprite` property. Only required if a `sprite` property is specified in the Mapbox Style object. |
| `spriteImageUrl` | `string` \| `Request` | `undefined` | Sprite image url for the sprite specified in the Mapbox Style object's `sprite` property. Only required if a `sprite` property is specified in the Mapbox Style object. |
| `spriteImageUrl` | `string` \| `Request` \| `Promise`<`string` \| `Request`> | `undefined` | Sprite image url for the sprite specified in the Mapbox Style object's `sprite` property. Only required if a `sprite` property is specified in the Mapbox Style object. |
| `getFonts` | (`arg0`: `string`\[], `arg1`: `string`) => `string`\[] | `undefined` | Function that receives a font stack and the url template from the GL style's `metadata['ol:webfonts']` property (if set) as arguments, and returns a (modified) font stack that is available. Font names are the names used in the Mapbox Style object. If not provided, the font stack will be used as-is. This function can also be used for loading web fonts. |
| `getImage?` | (`arg0`: `VectorLayer`<`any`> \| `VectorTileLayer`, `arg1`: `string`) => `string` \| `HTMLCanvasElement` \| `HTMLImageElement` | `undefined` | Function that returns an image or a URL for an image name. If the result is an HTMLImageElement, it must already be loaded. The layer can be used to call layer.changed() when the loading and processing of the image has finished. This function can be used for icons not in the sprite or to override sprite icons. |
| `...args` | `any` | `undefined` | - |
Expand Down Expand Up @@ -1167,11 +1167,11 @@ as object, when they contain a relative sprite url, or sources referencing data

#### transformRequest

• **transformRequest**: (`arg0`: `string`, `arg1`: [`ResourceType`](#ResourceType)) => `string` \| `void` \| `Request`
• **transformRequest**: (`arg0`: `string`, `arg1`: [`ResourceType`](#ResourceType)) => `string` \| `void` \| `Request` \| `Promise`<`string` \| `Request`>

##### Type declaration

▸ (`arg0`, `arg1`): `string` \| `void` \| `Request`
▸ (`arg0`, `arg1`): `string` \| `void` \| `Request` \| `Promise`<`string` \| `Request`>

Function for controlling how `ol-mapbox-style` fetches resources. Can be used for modifying
the url, adding headers or setting credentials options. Called with the url and the resource
Expand All @@ -1187,7 +1187,7 @@ the original request will not be modified.

###### Returns

`string` \| `void` \| `Request`
`string` \| `void` \| `Request` \| `Promise`<`string` \| `Request`>

<a name="modulesinternal_md"></a>

Expand Down
11 changes: 7 additions & 4 deletions src/apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ import {
/**
* @typedef {Object} Options
* @property {string} [accessToken] Access token for 'mapbox://' urls.
* @property {function(string, import("./util.js").ResourceType): (Request|string|void)} [transformRequest]
* @property {function(string, import("./util.js").ResourceType): (Request|string|Promise<Request|string>|void)} [transformRequest]
* Function for controlling how `ol-mapbox-style` fetches resources. Can be used for modifying
* the url, adding headers or setting credentials options. Called with the url and the resource
* type as arguments, this function is supposed to return a `Request` or a url `string`. Without a return value,
* the original request will not be modified.
* type as arguments, this function is supposed to return a `Request` or a url `string`, or a promise tehereof.
* Without a return value the original request will not be modified.
* @property {string} [projection='EPSG:3857'] Only useful when working with non-standard projections.
* Code of a projection registered with OpenLayers. All sources of the style must be provided in this
* projection. The projection must also have a valid extent defined, which will be used to determine the
Expand Down Expand Up @@ -419,7 +419,10 @@ export function applyStyle(
const transformed =
options.transformRequest(spriteImageUrl, 'SpriteImage') ||
spriteImageUrl;
if (transformed instanceof Request) {
if (
transformed instanceof Request ||
transformed instanceof Promise
) {
spriteImageUrl = transformed;
}
}
Expand Down
33 changes: 18 additions & 15 deletions src/stylefunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import RenderFeature from 'ol/render/Feature.js';
import Stroke from 'ol/style/Stroke.js';
import Style from 'ol/style/Style.js';
import Text from 'ol/style/Text.js';
import {toPromise} from 'ol/functions.js';

import Color from '@mapbox/mapbox-gl-style-spec/util/color.js';
import convertFunction from '@mapbox/mapbox-gl-style-spec/function/convert.js';
Expand Down Expand Up @@ -319,7 +320,7 @@ export const styleFunctionArgs = {};
* @param {Object} spriteData Sprite data from the url specified in
* the Mapbox Style object's `sprite` property. Only required if a `sprite`
* property is specified in the Mapbox Style object.
* @param {string|Request} spriteImageUrl Sprite image url for the sprite
* @param {string|Request|Promise<string|Request>} spriteImageUrl Sprite image url for the sprite
* specified in the Mapbox Style object's `sprite` property. Only required if a
* `sprite` property is specified in the Mapbox Style object.
* @param {function(Array<string>, string=):Array<string>} getFonts Function that
Expand Down Expand Up @@ -361,21 +362,23 @@ export function stylefunction(
if (typeof Image !== 'undefined') {
const img = new Image();
let blobUrl;
if (spriteImageUrl instanceof Request) {
fetch(spriteImageUrl)
.then((response) => response.blob())
.then((blob) => {
blobUrl = URL.createObjectURL(blob);
img.src = blobUrl;
})
.catch(() => {});
} else {
img.crossOrigin = 'anonymous';
img.src = spriteImageUrl;
if (blobUrl) {
URL.revokeObjectURL(blobUrl);
toPromise(() => spriteImageUrl).then((spriteImageUrl) => {
if (spriteImageUrl instanceof Request) {
fetch(spriteImageUrl)
.then((response) => response.blob())
.then((blob) => {
blobUrl = URL.createObjectURL(blob);
img.src = blobUrl;
})
.catch(() => {});
} else {
img.crossOrigin = 'anonymous';
img.src = spriteImageUrl;
if (blobUrl) {
URL.revokeObjectURL(blobUrl);
}
}
}
});
img.onload = function () {
spriteImage = img;
spriteImageSize = [img.width, img.height];
Expand Down
101 changes: 55 additions & 46 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {VectorTile} from 'ol';
import {expandUrl} from 'ol/tileurlfunction.js';
import {getUid} from 'ol/util.js';
import {normalizeSourceUrl, normalizeStyleUrl} from './mapbox.js';
import {toPromise} from 'ol/functions.js';

/** @typedef {'Style'|'Source'|'Sprite'|'SpriteImage'|'Tiles'|'GeoJSON'} ResourceType */

Expand Down Expand Up @@ -117,29 +118,33 @@ export function fetchResource(resourceType, url, options = {}, metadata) {
}
return pendingRequests[url][1];
}
let transformedRequest = options.transformRequest
const transformedRequest = options.transformRequest
? options.transformRequest(url, resourceType) || url
: url;
if (!(transformedRequest instanceof Request)) {
transformedRequest = new Request(transformedRequest);
}
if (!transformedRequest.headers.get('Accept')) {
transformedRequest.headers.set('Accept', 'application/json');
}
if (metadata) {
metadata.request = transformedRequest;
}
const pendingRequest = fetch(transformedRequest)
.then(function (response) {
delete pendingRequests[url];
return response.ok
? response.json()
: Promise.reject(new Error('Error fetching source ' + url));
})
.catch(function (error) {
delete pendingRequests[url];
return Promise.reject(new Error('Error fetching source ' + url));
});
const pendingRequest = toPromise(() => transformedRequest).then(
(transformedRequest) => {
if (!(transformedRequest instanceof Request)) {
transformedRequest = new Request(transformedRequest);
}
if (!transformedRequest.headers.get('Accept')) {
transformedRequest.headers.set('Accept', 'application/json');
}
if (metadata) {
metadata.request = transformedRequest;
}
return fetch(transformedRequest)
.then(function (response) {
delete pendingRequests[url];
return response.ok
? response.json()
: Promise.reject(new Error('Error fetching source ' + url));
})
.catch(function (error) {
delete pendingRequests[url];
return Promise.reject(new Error('Error fetching source ' + url));
});
}
);
pendingRequests[url] = [transformedRequest, pendingRequest];
return pendingRequest;
}
Expand Down Expand Up @@ -181,34 +186,38 @@ export function getTileJson(glSource, styleUrl, options = {}) {
: src;
if (tile instanceof VectorTile) {
tile.setLoader((extent, resolution, projection) => {
fetch(transformedRequest)
.then((response) => response.arrayBuffer())
.then((data) => {
const format = tile.getFormat();
const features = format.readFeatures(data, {
extent: extent,
featureProjection: projection,
});
// @ts-ignore
tile.setFeatures(features);
})
.catch((e) => tile.setState(TileState.ERROR));
toPromise(() => transformedRequest).then((transformedRequest) => {
fetch(transformedRequest)
.then((response) => response.arrayBuffer())
.then((data) => {
const format = tile.getFormat();
const features = format.readFeatures(data, {
extent: extent,
featureProjection: projection,
});
// @ts-ignore
tile.setFeatures(features);
})
.catch((e) => tile.setState(TileState.ERROR));
});
});
} else {
const img = tile.getImage();
if (transformedRequest instanceof Request) {
fetch(transformedRequest)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
img.addEventListener('load', () => URL.revokeObjectURL(url));
img.addEventListener('error', () => URL.revokeObjectURL(url));
img.src = url;
})
.catch((e) => tile.setState(TileState.ERROR));
} else {
img.src = transformedRequest;
}
toPromise(() => transformedRequest).then((transformedRequest) => {
if (transformedRequest instanceof Request) {
fetch(transformedRequest)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
img.addEventListener('load', () => URL.revokeObjectURL(url));
img.addEventListener('error', () => URL.revokeObjectURL(url));
img.src = url;
})
.catch((e) => tile.setState(TileState.ERROR));
} else {
img.src = transformedRequest;
}
});
}
};
}
Expand Down
24 changes: 16 additions & 8 deletions test/MapboxVectorLayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,28 @@ describe('ol/layer/MapboxVector', () => {
afterEach(function () {
window.fetch = originalFetch;
});
it('applies correct access token', function () {
it('applies correct access token', function (done) {
new MapboxVectorLayer({
styleUrl: 'mapbox://styles/mapbox/streets-v7',
accessToken: '123',
});
should(fetchUrl.url).eql(
'https://api.mapbox.com/styles/v1/mapbox/streets-v7?&access_token=123'
);
})
.getSource()
.once('change', () => {
should(fetchUrl.url).eql(
'https://api.mapbox.com/styles/v1/mapbox/streets-v7?&access_token=123'
);
done();
});
});
it('applies correct access token from url', function () {
it('applies correct access token from url', function (done) {
new MapboxVectorLayer({
styleUrl: 'foo?key=123',
});
should(fetchUrl.url).eql(`${location.origin}/foo?key=123`);
})
.getSource()
.once('change', () => {
should(fetchUrl.url).eql(`${location.origin}/foo?key=123`);
done();
});
});
});
});
28 changes: 16 additions & 12 deletions test/apply.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,14 @@ describe('ol-mapbox-style', function () {
1,
map.getView().getProjection()
);
window.fetch = originalFetch;
const url = new URL(requests[0].url);
const bbox = url.searchParams.get('bbox');
const extent = map.getView().calculateExtent();
should(bbox).be.equal(extent.join(','));
done();
source.once('change', () => {
window.fetch = originalFetch;
const url = new URL(requests[0].url);
const bbox = url.searchParams.get('bbox');
const extent = map.getView().calculateExtent();
should(bbox).be.equal(extent.join(','));
done();
});
})
.catch(done);
});
Expand Down Expand Up @@ -340,12 +342,14 @@ describe('ol-mapbox-style', function () {
1,
get('EPSG:3857')
);
window.fetch = originalFetch;
const url = new URL(requests[0].url);
should(url.searchParams.get('transformRequest')).be.equal('true');
should(source).be.instanceof(VectorSource);
should(layer.getStyle()).be.a.Function();
done();
source.once('change', () => {
window.fetch = originalFetch;
const url = new URL(requests[0].url);
should(url.searchParams.get('transformRequest')).be.equal('true');
should(source).be.instanceof(VectorSource);
should(layer.getStyle()).be.a.Function();
done();
});
})
.catch(done);
});
Expand Down
Loading