Skip to content

Commit d70c695

Browse files
author
Les Moffat
committed
Merge branch 'next' into release/3.9.0
2 parents 6fe9b37 + fb51c0e commit d70c695

File tree

5 files changed

+71
-37
lines changed

5 files changed

+71
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
- `ImageViewer.fitImageBounds(bounds)` fits the viewer to `bounds`.
1212
- `ImageViewerMarker` class added. `ImageViewer`can now have markers positioned in image pixels.
1313
- `ImageViewer` now has the `getCanvas` method to retrieve the `HTMLCanvasElement`used by the viewer.
14-
- Exports `ImageMarkerEvents` type
14+
- Exports `ImageMarkerEvents` type.
15+
- Configs passed to `setSpace` and `setHalo` are now validated ahead fo time.
1516

1617

1718
### 🐛 Bug Fixes
1819

1920
- Fixes a bug where `map.getProjection()` did not return a value when default projection was used
2021
- Fixes a bug where "Style Not Done Loading" error is thrown when an Image is used in conjunction with Spacebox.
2122
- Fixes a bug where switching between remote styles causes flickering in Halo.
23+
- Fixes a bug where Webgl would throw a texture error when two maps are rendered on the page due to a race condition loading images.
2224

2325
### ⚙️ Others
2426
- Right to left text is now opt-out. `rtlTextPlugin` can be passed in the constructor options to opt-out of installing the RTL text-plugin or install a different RTL text-plugin. Without this option the behaviour will remain the same.

src/Map.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ export class Map extends maplibregl.Map {
568568
this.setStyle(MapStyle.STREETS);
569569
warning += `Loading default MapTiler Cloud style "${MapStyle.STREETS.getDefaultVariant().getId()}" as a fallback.`;
570570
} else {
571-
warning += "Leaving the style as is.";
571+
warning += " Leaving the style as is.";
572572
}
573573
console.warn(warning);
574574
};
@@ -1207,17 +1207,21 @@ export class Map extends maplibregl.Map {
12071207
return;
12081208
}
12091209

1210-
const targetBeforeLayer = this.getLayersOrder()[0];
1211-
if (this.space) {
1212-
this.setSpaceFromStyle({ style: styleSpec });
1213-
} else {
1214-
this.initSpace({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.space });
1215-
}
1210+
try {
1211+
const targetBeforeLayer = this.getLayersOrder()[0];
1212+
if (this.space) {
1213+
this.setSpaceFromStyle({ style: styleSpec });
1214+
} else {
1215+
this.initSpace({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.space });
1216+
}
12161217

1217-
if (this.halo) {
1218-
this.setHaloFromStyle({ style: styleSpec });
1219-
} else {
1220-
this.initHalo({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.halo });
1218+
if (this.halo) {
1219+
this.setHaloFromStyle({ style: styleSpec });
1220+
} else {
1221+
this.initHalo({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.halo });
1222+
}
1223+
} catch (e) {
1224+
console.error(e);
12211225
}
12221226
};
12231227

@@ -1255,7 +1259,9 @@ export class Map extends maplibregl.Map {
12551259
// we have no way of knowing if the style is loaded or not
12561260
// which will fail internally if the style is not loaded correctly
12571261
handleStyleLoad();
1258-
} catch {}
1262+
} catch (e) {
1263+
console.error(e);
1264+
}
12591265

12601266
return this;
12611267
}

src/custom-layers/CubemapLayer/CubemapLayer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,12 @@ class CubemapLayer implements CustomLayerInterface {
276276
*/
277277
public onRemove(_map: MapSDK, gl: WebGLRenderingContext | WebGL2RenderingContext) {
278278
if (this.cubemap) {
279+
if (this.texture) {
280+
gl.deleteTexture(this.texture);
281+
}
279282
gl.deleteProgram(this.cubemap.shaderProgram);
280283
gl.deleteBuffer(this.cubemap.positionBuffer);
284+
this.texture = undefined;
281285
}
282286
}
283287

src/custom-layers/CubemapLayer/loadCubemapTexture.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { CubemapFaceNames, CubemapFaces } from "./types";
22

3+
type WebGLCtx = WebGLRenderingContext | WebGL2RenderingContext;
4+
35
interface LoadCubemapTextureOptions {
4-
gl: WebGLRenderingContext | WebGL2RenderingContext;
6+
gl: WebGLCtx;
57
faces?: CubemapFaces;
68
onReady: (texture: WebGLTexture, images?: HTMLImageElement[]) => void;
79
forceRefresh?: boolean;
810
}
911

1012
/**
1113
* Stores the result of the last successful execution of {@link loadCubemapTexture}.
12-
* @type {WebGLTexture | undefined}
14+
* @type {Map<WebGLCtx, WebGLTexture>}
1315
* @private
1416
*/
15-
let memoizedTexture: WebGLTexture | undefined = undefined;
17+
const memoizedTextures = new Map<WebGLCtx, WebGLTexture>();
1618

17-
let memoizedImages: HTMLImageElement[] | undefined = undefined;
19+
const memoizedImages = new Map<WebGLCtx, HTMLImageElement[]>();
1820
/**
1921
* Stores the stringified content of the 'faces' object from the last successful execution.
2022
* Used for memoization by {@link loadCubemapTexture}.
@@ -61,14 +63,15 @@ interface ImageLoadingPromiseReturnValue {
6163
* });
6264
*/
6365
export function loadCubemapTexture({ gl, faces, onReady, forceRefresh }: LoadCubemapTextureOptions) {
64-
if (memoizedTexture && !forceRefresh && facesKey === JSON.stringify(faces)) {
65-
onReady(memoizedTexture, memoizedImages);
66+
if (memoizedTextures.get(gl) && !forceRefresh && facesKey === JSON.stringify(faces)) {
67+
onReady(memoizedTextures.get(gl)!, memoizedImages.get(gl)!);
68+
return;
6669
}
6770

6871
facesKey = JSON.stringify(faces);
6972

70-
const texture = memoizedTexture ?? gl.createTexture();
71-
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
73+
const texture = memoizedTextures.get(gl) ?? gl.createTexture();
74+
// gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
7275

7376
if (!faces) {
7477
console.warn("[CubemapLayer][loadCubemapTexture]: Faces are null");
@@ -150,8 +153,8 @@ export function loadCubemapTexture({ gl, faces, onReady, forceRefresh }: LoadCub
150153

151154
onReady(texture, imageElements);
152155

153-
memoizedImages = imageElements;
154-
memoizedTexture = texture;
156+
memoizedImages.set(gl, imageElements);
157+
memoizedTextures.set(gl, texture);
155158
})
156159
.catch((error) => {
157160
console.error(`[CubemapLayer][loadCubemapTexture]: Error loading cubemap texture`, error);

src/custom-layers/RadialGradientLayer/RadialGradientLayer.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ export class RadialGradientLayer implements CustomLayerInterface {
130130
this.gradient = defaultConstructorOptions;
131131
return;
132132
}
133+
const errors = validateHaloSpecification(gradient);
134+
if (errors.length > 0) {
135+
throw new Error(`[RadialGradientLayer]: Invalid Halo specification:
136+
- ${errors.join("\n - ")}
137+
`);
138+
}
133139

134140
this.gradient = {
135141
...defaultConstructorOptions,
@@ -351,13 +357,11 @@ export class RadialGradientLayer implements CustomLayerInterface {
351357

352358
await this.animateOut();
353359

354-
if (!validateHaloSpecification(gradient)) {
355-
this.gradient.scale = defaultConstructorOptions.scale;
356-
this.gradient.stops = [
357-
[0, "transparent"],
358-
[1, "transparent"],
359-
];
360-
return;
360+
const errors = validateHaloSpecification(gradient);
361+
if (errors.length > 0) {
362+
throw new Error(`[RadialGradientLayer]: Invalid Halo specification:
363+
- ${errors.join("\n - ")}
364+
`);
361365
}
362366

363367
if (gradient === true) {
@@ -386,24 +390,39 @@ export class RadialGradientLayer implements CustomLayerInterface {
386390
}
387391
}
388392

389-
export function validateHaloSpecification(halo: RadialGradientLayerConstructorOptions | boolean): boolean {
393+
const validKeys = ["scale", "stops"];
394+
395+
export function validateHaloSpecification(halo: RadialGradientLayerConstructorOptions | boolean): Array<string> {
396+
const errors: string[] = [];
397+
390398
if (typeof halo === "boolean") {
391-
return true;
399+
return [];
400+
}
401+
402+
try {
403+
const additionalKeys = Object.keys(halo).filter((key) => !validKeys.includes(key));
404+
if (additionalKeys.length > 0) {
405+
errors.push(`Properties ${additionalKeys.map((key) => `\`${key}\``).join(", ")} are not supported.`);
406+
}
407+
} catch {
408+
errors.push("Halo specification is not an object.");
392409
}
393410

394411
if (typeof halo.scale !== "number") {
395-
return false;
412+
errors.push("Halo `scale` property is not a number.");
396413
}
397414

398415
// this is testing external data so we need to check
399416
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
400417
if (!halo.stops || halo.stops.length === 0) {
401-
return false;
418+
errors.push("Halo `stops` property is not an array.");
402419
}
403420

404-
if (halo.stops.some((stop) => typeof stop[0] !== "number" || typeof stop[1] !== "string")) {
405-
return false;
421+
// this is testing external data so we need to check
422+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
423+
if (halo.stops?.some((stop) => typeof stop[0] !== "number" || typeof stop[1] !== "string")) {
424+
errors.push("Halo `stops` property is not an array of [number, string]");
406425
}
407426

408-
return true;
427+
return errors;
409428
}

0 commit comments

Comments
 (0)