fix: guard deck layer arrays against null ids
This commit is contained in:
부모
b883c4113b
커밋
ccf3f2361f
@ -195,6 +195,33 @@ function getLayerId(value: unknown): string | null {
|
||||
return typeof candidate === "string" ? candidate : null;
|
||||
}
|
||||
|
||||
function sanitizeDeckLayerList(value: unknown): unknown[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
const seen = new Set<string>();
|
||||
const out: unknown[] = [];
|
||||
let dropped = 0;
|
||||
|
||||
for (const layer of value) {
|
||||
const layerId = getLayerId(layer);
|
||||
if (!layerId) {
|
||||
dropped += 1;
|
||||
continue;
|
||||
}
|
||||
if (seen.has(layerId)) {
|
||||
dropped += 1;
|
||||
continue;
|
||||
}
|
||||
seen.add(layerId);
|
||||
out.push(layer);
|
||||
}
|
||||
|
||||
if (dropped > 0 && import.meta.env.DEV) {
|
||||
console.warn(`Sanitized deck layer list: dropped ${dropped} invalid/duplicate entries`);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function normalizeAngleDeg(value: number, offset = 0): number {
|
||||
const v = value + offset;
|
||||
return ((v % 360) + 360) % 360;
|
||||
@ -3674,8 +3701,10 @@ export function Map3D({
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedLayers = sanitizeDeckLayerList(layers);
|
||||
|
||||
const deckProps = {
|
||||
layers,
|
||||
layers: normalizedLayers,
|
||||
getTooltip:
|
||||
projection === "globe"
|
||||
? undefined
|
||||
@ -3771,8 +3800,40 @@ export function Map3D({
|
||||
},
|
||||
} as const;
|
||||
|
||||
if (projection === "globe") globeDeckLayerRef.current?.setProps(deckProps);
|
||||
else overlayRef.current?.setProps(deckProps as unknown as never);
|
||||
const safeDeckProps = { ...deckProps, layers: normalizedLayers };
|
||||
const fallbackDeckProps = { ...safeDeckProps, layers: [] as unknown[] };
|
||||
const applyDeckProps = () => {
|
||||
if (projection === "globe") {
|
||||
const target = globeDeckLayerRef.current;
|
||||
if (!target) return;
|
||||
try {
|
||||
target.setProps(safeDeckProps as never);
|
||||
} catch (e) {
|
||||
console.error("Failed to apply deck props on globe overlay. Falling back to empty deck layer set.", e);
|
||||
try {
|
||||
target.setProps(fallbackDeckProps as never);
|
||||
} catch {
|
||||
// Ignore secondary failure; rendering will recover on next update.
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const target = overlayRef.current;
|
||||
if (!target) return;
|
||||
try {
|
||||
target.setProps(safeDeckProps as unknown as never);
|
||||
} catch (e) {
|
||||
console.error("Failed to apply deck props on mercator overlay. Falling back to empty deck layer set.", e);
|
||||
try {
|
||||
target.setProps(fallbackDeckProps as unknown as never);
|
||||
} catch {
|
||||
// Ignore secondary failure.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
applyDeckProps();
|
||||
}, [
|
||||
projection,
|
||||
shipData,
|
||||
|
||||
@ -24,6 +24,7 @@ class MatrixView extends View<MatrixViewState> {
|
||||
}
|
||||
|
||||
const IDENTITY_4x4: number[] = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
type DeckLayerList = NonNullable<DeckProps<MatrixView[]>["layers"]>;
|
||||
|
||||
function readMat4(m: ArrayLike<number>): number[] {
|
||||
const out = new Array<number>(16);
|
||||
@ -40,6 +41,25 @@ function mat4Changed(a: number[] | undefined, b: ArrayLike<number>): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDeckLayerId(value: unknown): string | null {
|
||||
if (!value || typeof value !== "object") return null;
|
||||
const candidate = (value as { id?: unknown }).id;
|
||||
return typeof candidate === "string" ? candidate : null;
|
||||
}
|
||||
|
||||
function sanitizeDeckLayers(value: unknown): DeckLayerList {
|
||||
if (!Array.isArray(value)) return [] as DeckLayerList;
|
||||
const out: DeckLayerList = [];
|
||||
const seen = new Set<string>();
|
||||
for (const item of value) {
|
||||
const layerId = getDeckLayerId(item);
|
||||
if (!layerId || seen.has(layerId)) continue;
|
||||
seen.add(layerId);
|
||||
out.push(item as DeckLayerList[number]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export class MaplibreDeckCustomLayer implements maplibregl.CustomLayerInterface {
|
||||
id: string;
|
||||
type = "custom" as const;
|
||||
@ -67,7 +87,8 @@ export class MaplibreDeckCustomLayer implements maplibregl.CustomLayerInterface
|
||||
}
|
||||
|
||||
setProps(next: Partial<DeckProps<MatrixView[]>>) {
|
||||
this._deckProps = { ...this._deckProps, ...next };
|
||||
const normalized = next.layers ? { ...next, layers: sanitizeDeckLayers(next.layers) } : next;
|
||||
this._deckProps = { ...this._deckProps, ...normalized };
|
||||
if (this._deck) this._deck.setProps(this._deckProps as DeckProps<MatrixView[]>);
|
||||
this._map?.triggerRepaint();
|
||||
}
|
||||
@ -80,17 +101,20 @@ export class MaplibreDeckCustomLayer implements maplibregl.CustomLayerInterface
|
||||
// Re-attached after a style change; keep the existing Deck instance so we don't reuse
|
||||
// finalized Layer objects (Deck does not allow that).
|
||||
this._lastMvp = undefined;
|
||||
this._deck.setProps({
|
||||
const nextDeckProps = {
|
||||
...this._deckProps,
|
||||
layers: sanitizeDeckLayers(this._deckProps.layers),
|
||||
canvas: map.getCanvas(),
|
||||
// Ensure any pending redraw requests trigger a map repaint again.
|
||||
_customRender: () => map.triggerRepaint(),
|
||||
} as DeckProps<MatrixView[]>);
|
||||
};
|
||||
this._deck.setProps(nextDeckProps as DeckProps<MatrixView[]>);
|
||||
return;
|
||||
}
|
||||
|
||||
const deck = new Deck<MatrixView[]>({
|
||||
...this._deckProps,
|
||||
layers: sanitizeDeckLayers(this._deckProps.layers),
|
||||
// Share MapLibre's WebGL context + canvas (single context).
|
||||
gl: gl as WebGL2RenderingContext,
|
||||
canvas: map.getCanvas(),
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user