Skip to content
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
# MapTiler SDK Changelog

## NEXT
## LATEST

### ✨ Features and improvements
None

### 🐛 Bug Fixes
- 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.

### ⚙️ Others
None



## NEXT (3.9.0)

### ✨ Features and improvements
- Additions and improvements to ImageViewer
Expand Down
30 changes: 18 additions & 12 deletions src/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ export class Map extends maplibregl.Map {
this.setStyle(MapStyle.STREETS);
warning += `Loading default MapTiler Cloud style "${MapStyle.STREETS.getDefaultVariant().getId()}" as a fallback.`;
} else {
warning += "Leaving the style as is.";
warning += " Leaving the style as is.";
}
console.warn(warning);
};
Expand Down Expand Up @@ -1207,17 +1207,21 @@ export class Map extends maplibregl.Map {
return;
}

const targetBeforeLayer = this.getLayersOrder()[0];
if (this.space) {
this.setSpaceFromStyle({ style: styleSpec });
} else {
this.initSpace({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.space });
}
try {
const targetBeforeLayer = this.getLayersOrder()[0];
if (this.space) {
this.setSpaceFromStyle({ style: styleSpec });
} else {
this.initSpace({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.space });
}

if (this.halo) {
this.setHaloFromStyle({ style: styleSpec });
} else {
this.initHalo({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.halo });
if (this.halo) {
this.setHaloFromStyle({ style: styleSpec });
} else {
this.initHalo({ before: targetBeforeLayer, spec: styleSpec.metadata?.maptiler?.halo });
}
} catch (e) {
console.error(e);
}
};

Expand Down Expand Up @@ -1255,7 +1259,9 @@ export class Map extends maplibregl.Map {
// we have no way of knowing if the style is loaded or not
// which will fail internally if the style is not loaded correctly
handleStyleLoad();
} catch {}
} catch (e) {
console.error(e);
}

return this;
}
Expand Down
4 changes: 4 additions & 0 deletions src/custom-layers/CubemapLayer/CubemapLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,12 @@ class CubemapLayer implements CustomLayerInterface {
*/
public onRemove(_map: MapSDK, gl: WebGLRenderingContext | WebGL2RenderingContext) {
if (this.cubemap) {
if (this.texture) {
gl.deleteTexture(this.texture);
}
gl.deleteProgram(this.cubemap.shaderProgram);
gl.deleteBuffer(this.cubemap.positionBuffer);
this.texture = undefined;
}
}

Expand Down
23 changes: 13 additions & 10 deletions src/custom-layers/CubemapLayer/loadCubemapTexture.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { CubemapFaceNames, CubemapFaces } from "./types";

type WebGLCtx = WebGLRenderingContext | WebGL2RenderingContext;

interface LoadCubemapTextureOptions {
gl: WebGLRenderingContext | WebGL2RenderingContext;
gl: WebGLCtx;
faces?: CubemapFaces;
onReady: (texture: WebGLTexture, images?: HTMLImageElement[]) => void;
forceRefresh?: boolean;
}

/**
* Stores the result of the last successful execution of {@link loadCubemapTexture}.
* @type {WebGLTexture | undefined}
* @type {Map<WebGLCtx, WebGLTexture>}
* @private
*/
let memoizedTexture: WebGLTexture | undefined = undefined;
const memoizedTextures = new Map<WebGLCtx, WebGLTexture>();

let memoizedImages: HTMLImageElement[] | undefined = undefined;
const memoizedImages = new Map<WebGLCtx, HTMLImageElement[]>();
/**
* Stores the stringified content of the 'faces' object from the last successful execution.
* Used for memoization by {@link loadCubemapTexture}.
Expand Down Expand Up @@ -61,14 +63,15 @@ interface ImageLoadingPromiseReturnValue {
* });
*/
export function loadCubemapTexture({ gl, faces, onReady, forceRefresh }: LoadCubemapTextureOptions) {
if (memoizedTexture && !forceRefresh && facesKey === JSON.stringify(faces)) {
onReady(memoizedTexture, memoizedImages);
if (memoizedTextures.get(gl) && !forceRefresh && facesKey === JSON.stringify(faces)) {
onReady(memoizedTextures.get(gl)!, memoizedImages.get(gl)!);
return;
}

facesKey = JSON.stringify(faces);

const texture = memoizedTexture ?? gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
const texture = memoizedTextures.get(gl) ?? gl.createTexture();
// gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);

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

onReady(texture, imageElements);

memoizedImages = imageElements;
memoizedTexture = texture;
memoizedImages.set(gl, imageElements);
memoizedTextures.set(gl, texture);
})
.catch((error) => {
console.error(`[CubemapLayer][loadCubemapTexture]: Error loading cubemap texture`, error);
Expand Down
47 changes: 33 additions & 14 deletions src/custom-layers/RadialGradientLayer/RadialGradientLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ export class RadialGradientLayer implements CustomLayerInterface {
this.gradient = defaultConstructorOptions;
return;
}
const errors = validateHaloSpecification(gradient);
if (errors.length > 0) {
throw new Error(`[RadialGradientLayer]: Invalid Halo specification:
- ${errors.join("\n - ")}
`);
}

this.gradient = {
...defaultConstructorOptions,
Expand Down Expand Up @@ -351,13 +357,11 @@ export class RadialGradientLayer implements CustomLayerInterface {

await this.animateOut();

if (!validateHaloSpecification(gradient)) {
this.gradient.scale = defaultConstructorOptions.scale;
this.gradient.stops = [
[0, "transparent"],
[1, "transparent"],
];
return;
const errors = validateHaloSpecification(gradient);
if (errors.length > 0) {
throw new Error(`[RadialGradientLayer]: Invalid Halo specification:
- ${errors.join("\n - ")}
`);
}

if (gradient === true) {
Expand Down Expand Up @@ -386,24 +390,39 @@ export class RadialGradientLayer implements CustomLayerInterface {
}
}

export function validateHaloSpecification(halo: RadialGradientLayerConstructorOptions | boolean): boolean {
const validKeys = ["scale", "stops"];

export function validateHaloSpecification(halo: RadialGradientLayerConstructorOptions | boolean): Array<string> {
const errors: string[] = [];

if (typeof halo === "boolean") {
return true;
return [];
}

try {
const additionalKeys = Object.keys(halo).filter((key) => !validKeys.includes(key));
if (additionalKeys.length > 0) {
errors.push(`Properties ${additionalKeys.map((key) => `\`${key}\``).join(", ")} are not supported.`);
}
} catch {
errors.push("Halo specification is not an object.");
}

if (typeof halo.scale !== "number") {
return false;
errors.push("Halo `scale` property is not a number.");
}

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

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

return true;
return errors;
}
Loading