diff --git a/.eslintrc.js b/.eslintrc.js index f59cbec36..ac5dbff8f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, parser: "@typescript-eslint/parser", extends: ["standard-with-typescript", "plugin:react/recommended"], - // ignorePatterns: ["**/*.html"], + ignorePatterns: ["**/*.slpk"], overrides: [ { env: { diff --git a/package.json b/package.json index 65a147f58..516fac195 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,11 @@ "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/react-fontawesome": "^0.1.17", "@hyperjump/json-schema": "^0.23.2", - "@loaders.gl/3d-tiles": "^4.1.0-alpha.11", - "@loaders.gl/core": "^4.1.0-alpha.11", - "@loaders.gl/i3s": "^4.1.0-alpha.11", - "@loaders.gl/tiles": "^4.1.0-alpha.11", + "@loaders.gl/loader-utils": "^4.3.3", + "@loaders.gl/3d-tiles": "^4.3.3", + "@loaders.gl/core": "^4.3.3", + "@loaders.gl/i3s": "^4.3.3", + "@loaders.gl/tiles": "^4.3.3", "@luma.gl/core": "^8.5.14", "@math.gl/core": "^3.6.0", "@math.gl/proj4": "^3.6.3", @@ -113,7 +114,7 @@ "webpack-dev-server": "^5.0.4" }, "resolutions": { - "@loaders.gl/tiles": "^4.1.0-alpha.11" + "@loaders.gl/tiles": "^4.3.3" }, "volta": { "node": "18.18.2", diff --git a/src/components/bookmarks-panel/bookmarks-panel.spec.tsx b/src/components/bookmarks-panel/bookmarks-panel.spec.tsx index 08cdf2670..a1364469c 100644 --- a/src/components/bookmarks-panel/bookmarks-panel.spec.tsx +++ b/src/components/bookmarks-panel/bookmarks-panel.spec.tsx @@ -11,6 +11,12 @@ jest.mock("../../utils/hooks/layout"); const dragAndDropText = "Drag and drop your json file here"; +Object.defineProperty(globalThis, "crypto", { + value: { + randomUUID: () => "", + }, +}); + const TEST_BOOKMARKS = [ { id: "testId1", diff --git a/src/components/bookmarks-panel/bookmarks-panel.tsx b/src/components/bookmarks-panel/bookmarks-panel.tsx index 9bffa2e0f..0902b142a 100644 --- a/src/components/bookmarks-panel/bookmarks-panel.tsx +++ b/src/components/bookmarks-panel/bookmarks-panel.tsx @@ -284,9 +284,11 @@ export const BookmarksPanel = ({ onEditBookmarks={onEditBookmarksClickHandler} onClearBookmarks={onClearBookmarksClickHandler} onUploadBookmarks={() => { - setPopoverType( - bookmarks.length ? PopoverType.uploadWarning : PopoverType.upload - ); + setTimeout(() => { + setPopoverType( + bookmarks.length ? PopoverType.uploadWarning : PopoverType.upload + ); + }, 1); }} onDownloadBookmarks={onDownloadBookmarks} onCollapsed={onCollapsed} diff --git a/src/components/bookmarks-panel/unsaved-bookmark-warning.spec.tsx b/src/components/bookmarks-panel/unsaved-bookmark-warning.spec.tsx index 1a9313364..0c48ec3cd 100644 --- a/src/components/bookmarks-panel/unsaved-bookmark-warning.spec.tsx +++ b/src/components/bookmarks-panel/unsaved-bookmark-warning.spec.tsx @@ -19,6 +19,6 @@ describe("UnsavedBookmarkWarning", () => { const { container } = callRender(renderWithTheme); const fileInteractionContiner = container.firstChild.firstChild; expect(container.firstChild).toBeInTheDocument(); - expect(fileInteractionContiner.childNodes.length).toBe(3); + expect(fileInteractionContiner.childNodes.length).toBe(2); }); }); diff --git a/src/components/comparison/comparison-side/comparison-side.tsx b/src/components/comparison/comparison-side/comparison-side.tsx index 6323a648a..b009dcf27 100644 --- a/src/components/comparison/comparison-side/comparison-side.tsx +++ b/src/components/comparison/comparison-side/comparison-side.tsx @@ -36,6 +36,7 @@ import { parseTilesetUrlParams } from "../../../utils/url-utils"; import { findExampleAndUpdateWithViewState, getActiveLayersByIds, + getLayerUrl, handleSelectAllLeafsInGroup, selectNestedLayers, } from "../../../utils/layer-utils"; @@ -234,7 +235,13 @@ export const ComparisonSide = ({ const tilesetsData: TilesetMetadata[] = []; for (const layer of activeLayers) { - const params = parseTilesetUrlParams(layer.url, layer); + let params = { tilesetUrl: "" as string | File, token: "" }; + if (typeof layer.url === "string") { + params = parseTilesetUrlParams(layer.url, layer); + } else { + params.tilesetUrl = layer.url; + } + const { tilesetUrl, token } = params; tilesetsData.push({ @@ -254,8 +261,9 @@ export const ComparisonSide = ({ }) ); if (buildingExplorerOpened && tilesetsData[0]) { + const tilesetDataUrl = getLayerUrl(tilesetsData[0].url); void dispatch( - getBSLStatisticsSummary({ statSummaryUrl: tilesetsData[0].url, side }) + getBSLStatisticsSummary({ statSummaryUrl: tilesetDataUrl, side }) ); } }, [activeLayers, buildingExplorerOpened]); @@ -279,7 +287,7 @@ export const ComparisonSide = ({ if (loadedTilesetsCount && activeLayersCount) { const isBSL = loadedTilesetsCount > activeLayersCount; - let statsName = activeLayers[0].url; + let statsName = getLayerUrl(activeLayers[0].url); if (!isBSL) { statsName = loadedTilesets @@ -312,12 +320,13 @@ export const ComparisonSide = ({ .map((sublayer) => ({ id: sublayer.id, url: sublayer.url, + fetch: sublayer.fetch, token: sublayer.token, type: sublayer.type ?? TilesetType.I3S, })); }; - const onTraversalCompleteHandler = (selectedTiles) => { + const onTraversalCompleteHandler = (selectedTiles: Tile3D[]) => { // A parent tileset of selected tiles const aTileset = selectedTiles?.[0]?.tileset; // Make sure that the actual tileset has been traversed traversed diff --git a/src/components/deck-gl-wrapper/deck-gl-wrapper.tsx b/src/components/deck-gl-wrapper/deck-gl-wrapper.tsx index 01c46f66b..eefb35345 100644 --- a/src/components/deck-gl-wrapper/deck-gl-wrapper.tsx +++ b/src/components/deck-gl-wrapper/deck-gl-wrapper.tsx @@ -62,6 +62,7 @@ interface DeckGlI3sProps { layers3d: Array<{ id?: number; url?: string; + fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); token?: string | null; type: TilesetType; }>; diff --git a/src/components/layers-panel/insert-panel/insert-panel.spec.tsx b/src/components/layers-panel/insert-panel/insert-panel.spec.tsx index fd20dc266..0b9e68498 100644 --- a/src/components/layers-panel/insert-panel/insert-panel.spec.tsx +++ b/src/components/layers-panel/insert-panel/insert-panel.spec.tsx @@ -10,6 +10,12 @@ import "@testing-library/jest-dom"; const onInsertMock = jest.fn(); const onCancelMock = jest.fn(); +Object.defineProperty(globalThis, "crypto", { + value: { + randomUUID: () => "", + }, +}); + const callRender = ( renderFunc, props = {}, diff --git a/src/components/layers-panel/insert-panel/insert-panel.tsx b/src/components/layers-panel/insert-panel/insert-panel.tsx index cfd4ffe2a..02a86d069 100644 --- a/src/components/layers-panel/insert-panel/insert-panel.tsx +++ b/src/components/layers-panel/insert-panel/insert-panel.tsx @@ -7,6 +7,7 @@ import { type LayoutProps, TilesetType, BaseMapGroup, + FileType, } from "../../../types"; import { getCurrentLayoutProperty, @@ -23,13 +24,15 @@ import { getLayerNameInfo, selectLayerNames, } from "../../../redux/slices/layer-names-slice"; +import { UploadPanel } from "../../upload-panel/upload-panel"; +import { getLayerUrl } from "../../../utils/layer-utils"; const NO_NAME_ERROR = "Please enter name"; const INVALID_URL_ERROR = "Invalid URL"; export interface CustomLayerData { name: string; - url: string; + url: string | File; token?: string; group?: BaseMapGroup; } @@ -37,6 +40,7 @@ export interface CustomLayerData { interface InsertLayerProps { title: string; groups?: string[]; + noFile?: boolean; onInsert: (object: CustomLayerData) => Promise | void; onCancel: () => void; children?: React.ReactNode; @@ -101,13 +105,14 @@ const SpinnerContainer = styled.div` export const InsertPanel = ({ title, groups, + noFile, onInsert, onCancel, children = null, }: InsertLayerProps) => { const [group, setGroup] = useState(BaseMapGroup.Maplibre); const [name, setName] = useState(""); - const [url, setUrl] = useState(""); + const [url, setUrl] = useState(""); const [token, setToken] = useState(""); const [nameError, setNameError] = useState(""); @@ -116,21 +121,25 @@ export const InsertPanel = ({ const layerNames = useAppSelector(selectLayerNames); const dispatch = useAppDispatch(); + const getNewLayerUrl = () => getLayerUrl(url); + const validateFields = (): void => { let isFormValid = true; - const type = getTilesetType(url); + const type = getTilesetType(typeof url === "string" ? url : url.name); - try { - // eslint-disable-next-line no-new - new URL(url); - } catch (_) { - setUrlError(INVALID_URL_ERROR); - isFormValid = false; + if (typeof url === "string") { + try { + // eslint-disable-next-line no-new + new URL(url); + } catch (_) { + setUrlError(INVALID_URL_ERROR); + isFormValid = false; + } } if ( (type !== TilesetType.I3S && !name) || - (type === TilesetType.I3S && !name && !layerNames[url]?.name) + (type === TilesetType.I3S && !name && !layerNames[getNewLayerUrl()]?.name) ) { setNameError(NO_NAME_ERROR); isFormValid = false; @@ -138,7 +147,7 @@ export const InsertPanel = ({ if (isFormValid) { void onInsert({ - name: name || layerNames[url]?.name, + name: name || layerNames[getNewLayerUrl()]?.name, url, token, group: groups ? group : undefined, @@ -147,16 +156,16 @@ export const InsertPanel = ({ }; useEffect(() => { - const type = getTilesetType(url); + const type = getTilesetType(getNewLayerUrl()); if (isValidateInProgress && type === TilesetType.I3S) { if ( - (layerNames[url] !== undefined && - layerNames[url].status === FetchingStatus.ready) || + (layerNames[getNewLayerUrl()] !== undefined && + layerNames[getNewLayerUrl()].status === FetchingStatus.ready) || name.length > 0 ) { setValidateInProgress(false); validateFields(); - } else if (!layerNames[url]) { + } else if (!layerNames[getNewLayerUrl()]) { void dispatch(getLayerNameInfo({ layerUrl: url, token, type })); } } @@ -165,7 +174,7 @@ export const InsertPanel = ({ const handleInsert = (event) => { event.preventDefault(); - if (getTilesetType(url) !== TilesetType.I3S) { + if (getTilesetType(getNewLayerUrl()) !== TilesetType.I3S) { validateFields(); } else { setValidateInProgress(true); @@ -173,11 +182,11 @@ export const InsertPanel = ({ }; const handleInputChange = (event) => { - const { name, value } = event.target; + const { name: fieldName, value, files } = event.target; - switch (name) { + switch (fieldName) { case "BasemapProvider": - setGroup(value); + setGroup(value as BaseMapGroup); setNameError(""); break; case "Name": @@ -185,7 +194,14 @@ export const InsertPanel = ({ setNameError(""); break; case "URL": - setUrl(value); + if (files) { + setUrl(files[0]); + if (!name) { + setName(files[0].name.substring(0, files[0].name.length - 5)); + } + } else { + setUrl(value); + } setUrlError(""); break; case "Token": @@ -225,7 +241,7 @@ export const InsertPanel = ({ @@ -235,6 +251,13 @@ export const InsertPanel = ({ value={token} onChange={handleInputChange} /> + {!noFile && { handleInputChange({ target: { files, name: "URL" } }); }} + fileType={FileType.binary} + />} void; } +const getPath = (url: string | File) => getLayerUrl(url); + export const LayersPanel = ({ id, pageId, @@ -208,7 +211,7 @@ export const LayersPanel = ({ const handleInsertLayer = (layer: CustomLayerData) => { const existedLayer = layers.some( - (exisLayer) => exisLayer.url.trim() === layer.url.trim() + (exisLayer) => getPath(exisLayer.url).trim() === getPath(layer.url).trim() ); if (existedLayer) { @@ -216,12 +219,13 @@ export const LayersPanel = ({ return; } - const id = layer.url.replace(/" "/g, "-"); + const id = getPath(layer.url).replace(/" "/g, "-"); const newLayer: LayerExample = { ...layer, + url: layer.url, id, custom: true, - type: getTilesetType(layer.url), + type: getTilesetType(getPath(layer.url)), }; onLayerInsert(newLayer); @@ -250,7 +254,7 @@ export const LayersPanel = ({ return layersList; }; - const saveLayerTypes = (layers) => { + const saveLayerTypes = (layers: OperationalLayer[]) => { const layerTypes: string[] = []; if (layers) { for (const layer of layers) { @@ -264,10 +268,10 @@ export const LayersPanel = ({ // TODO Add loader to show webscene loading const handleInsertScene = async (scene: CustomLayerData): Promise => { - scene.url = convertUrlToRestFormat(scene.url); + scene.url = convertUrlToRestFormat(getPath(scene.url)); const existedScene = layers.some( - (exisLayer) => exisLayer.url.trim() === scene.url.trim() + (exisLayer) => getPath(exisLayer.url).trim() === getPath(scene.url).trim() ); if (existedScene) { @@ -291,7 +295,8 @@ export const LayersPanel = ({ const newLayer: LayerExample = { ...scene, - id: scene.url, + url: getPath(scene.url), + id: getPath(scene.url), custom: true, layers: webSceneLayerExamples, type: getTilesetType(scene.url), @@ -322,7 +327,7 @@ export const LayersPanel = ({ switch (error.message) { case "NO_AVAILABLE_SUPPORTED_LAYERS_ERROR": { const layers = (error as LayerError).details; - saveLayerTypes(layers); + saveLayerTypes(layers as OperationalLayer[]); setShowNoSupportedLayersInSceneError(true); break; } @@ -341,11 +346,11 @@ export const LayersPanel = ({ }; const handleInsertMap = (map: CustomLayerData): void => { - const id = map.url.replace(/" "/g, "-"); + const id = getPath(map.url).replace(/" "/g, "-"); if (map.group !== undefined) { const newMap: BaseMap = { id, - mapUrl: map.url, + mapUrl: getPath(map.url), name: map.name, token: map.token, iconId: "Custom", @@ -513,6 +518,7 @@ export const LayersPanel = ({ { await handleInsertScene(scene); }} @@ -542,6 +548,7 @@ export const LayersPanel = ({ { handleInsertMap(map); }} diff --git a/src/components/upload-panel/upload-panel-item.tsx b/src/components/upload-panel/upload-panel-item.tsx index cd6c74dcd..574cd51bf 100644 --- a/src/components/upload-panel/upload-panel-item.tsx +++ b/src/components/upload-panel/upload-panel-item.tsx @@ -1,13 +1,11 @@ import styled from "styled-components"; import { ActionButton } from "../action-button/action-button"; import { ActionButtonVariant, type LayoutProps } from "../../types"; -import WarningIcon from "../../../public/icons/warning.svg"; import { useAppLayout } from "../../utils/hooks/layout"; -const Container = styled.div` +const Container = styled.div` width: 295px; - height: 329px; - padding: 16px; + padding: ${({ noPadding }) => noPadding ? "0px" : "16px"}; border-radius: 8px; background-color: ${({ theme }) => theme.colors.mainColor}; `; @@ -25,6 +23,7 @@ const Title = styled.div` font-weight: 700; font-size: 16px; line-height: 19px; + margin-bottom: 44px; color: ${({ theme }) => theme.colors.fontColor}; `; @@ -32,37 +31,40 @@ const ButtonsContainer = styled.div<{ justify: string }>` display: flex; flex-direction: row; justify-content: ${(props) => props.justify}; + margin-top: 44px; `; interface UploadPanelItemProps { title?: string; children: React.ReactNode; - onCancel: () => void; + noPadding?: boolean; + onCancel?: () => void; onConfirm?: () => void; } export const UploadPanelItem = ({ title, children, + noPadding, onCancel, onConfirm, }: UploadPanelItemProps) => { const layout = useAppLayout(); return ( - + - {title ?? <WarningIcon />} + {title && {title}} {children} - - + {onCancel && Cancel - + } {onConfirm && Next} - + } ); diff --git a/src/components/upload-panel/upload-panel.spec.tsx b/src/components/upload-panel/upload-panel.spec.tsx index 0a988cc7d..0d1c6d301 100644 --- a/src/components/upload-panel/upload-panel.spec.tsx +++ b/src/components/upload-panel/upload-panel.spec.tsx @@ -15,6 +15,12 @@ jest.mock("@hyperjump/json-schema", () => ({ ), })); +Object.defineProperty(globalThis, "crypto", { + value: { + randomUUID: () => "", + }, +}); + const onCancel = jest.fn(); const onFileUploaded = jest.fn(); diff --git a/src/components/upload-panel/upload-panel.tsx b/src/components/upload-panel/upload-panel.tsx index f0302f620..388a5caf5 100644 --- a/src/components/upload-panel/upload-panel.tsx +++ b/src/components/upload-panel/upload-panel.tsx @@ -5,11 +5,9 @@ import { UploadPanelItem } from "./upload-panel-item"; import UploadIcon from "../../../public/icons/upload.svg"; import { Layout } from "../../utils/enums"; -import { useRef, useState } from "react"; +import { useMemo, useRef, useState } from "react"; import { useAppLayout } from "../../utils/hooks/layout"; -const UPLOAD_INPUT_ID = "upload-file-input"; - const FileInteractionContainer = styled.label` box-sizing: border-box; display: flex; @@ -45,6 +43,11 @@ const DragAndDropFileText = styled(FileTextItem)` color: ${({ theme }) => theme.colors.fontColor}; `; +const UploadedFileText = styled(FileTextItem)` + color: ${({ theme }) => theme.colors.fontColor}; + word-break: break-all; +`; + const BrosweFileText = styled(FileTextItem)` color: ${({ theme }) => theme.colors.mainDimColorInverted}; `; @@ -56,12 +59,15 @@ const UploadInput = styled.input` `; interface UploadProps { - title: string; + title?: string; dragAndDropText: string; fileType: FileType; multipleFiles?: boolean; - onCancel: () => void; - onFileUploaded: (fileUploaded: FileUploaded) => Promise; + noPadding?: boolean; + accept?: string; + onCancel?: () => void; + onFileUploaded?: (fileUploaded: FileUploaded) => Promise | void; + onFileEvent?: (files: FileList) => void; } export const UploadPanel = ({ @@ -69,11 +75,17 @@ export const UploadPanel = ({ dragAndDropText, fileType, multipleFiles, + noPadding, + accept, onCancel, onFileUploaded, + onFileEvent, }: UploadProps) => { + const UPLOAD_INPUT_ID = useMemo(() => `upload-file-input${crypto.randomUUID()}`, []); + const layout = useAppLayout(); const [dragActive, setDragActive] = useState(false); + const [fileUploaded, setFileUploaded] = useState(""); const inputRef = useRef(null); const readFile = async (files: FileList) => { @@ -86,7 +98,7 @@ export const UploadPanel = ({ const info: Record = { url: file.name, }; - void onFileUploaded({ fileContent: event?.target?.result, info }); + await onFileUploaded?.({ fileContent: event?.target?.result, info }); }; if (fileType === FileType.binary) { reader.readAsArrayBuffer(file); @@ -111,6 +123,11 @@ export const UploadPanel = ({ e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files?.[0]) { + if (accept && !e.dataTransfer.files?.[0].name.endsWith(accept)) { + return; + } + setFileUploaded(e.dataTransfer.files[0].name); + onFileEvent?.(e.dataTransfer.files); readFile(e.dataTransfer.files).catch(() => { console.error("Read uploaded file operation error"); }); @@ -122,6 +139,8 @@ export const UploadPanel = ({ ) { e.preventDefault(); if (e.target.files?.[0]) { + setFileUploaded(e.target.files[0].name); + onFileEvent?.(e.target.files); readFile(e.target.files).catch(() => { console.error("Read uploaded file operation error"); }); @@ -129,35 +148,46 @@ export const UploadPanel = ({ }; return ( - + - - {layout === Layout.Desktop && ( + {!fileUploaded && ( <> - {dragAndDropText} - or + + {layout === Layout.Desktop && ( + <> + {dragAndDropText} + or + + )} + browse file + {dragActive && ( + + )} )} - browse file - {dragActive && ( - + {fileUploaded && ( + <> + + {fileUploaded} + )} diff --git a/src/pages/debug-app/debug-app.tsx b/src/pages/debug-app/debug-app.tsx index e393bc840..062d4a8a4 100644 --- a/src/pages/debug-app/debug-app.tsx +++ b/src/pages/debug-app/debug-app.tsx @@ -62,6 +62,7 @@ import { useAppLayout } from "../../utils/hooks/layout"; import { findExampleAndUpdateWithViewState, getActiveLayersByIds, + getLayerUrl, handleSelectAllLeafsInGroup, initActiveLayer, selectNestedLayers, @@ -191,6 +192,7 @@ export const DebugApp = () => { .map((sublayer) => ({ id: sublayer.id, url: sublayer.url, + fetch: sublayer.fetch, token: sublayer.token, type: sublayer.type ?? TilesetType.I3S, })); @@ -218,7 +220,8 @@ export const DebugApp = () => { * Hook for start using tilesets stats. */ useEffect(() => { - const tilesetsStats = initStats(activeLayers[0]?.url); + const activeLayerPath = activeLayers[0] ? getLayerUrl(activeLayers[0].url) : undefined; + const tilesetsStats = initStats(activeLayerPath); setTilesetsStats(tilesetsStats); }, [loadedTilesets]); @@ -232,7 +235,13 @@ export const DebugApp = () => { const tilesetsData: TilesetMetadata[] = []; for (const layer of activeLayers) { - const params = parseTilesetUrlParams(layer.url, layer); + let params = { tilesetUrl: "" as string | File, token: "" }; + if (typeof layer.url === "string") { + params = parseTilesetUrlParams(layer.url, layer); + } else { + params.tilesetUrl = layer.url; + } + const { tilesetUrl, token } = params; tilesetsData.push({ diff --git a/src/pages/viewer-app/viewer-app.tsx b/src/pages/viewer-app/viewer-app.tsx index d44e006c8..d52f6f53e 100644 --- a/src/pages/viewer-app/viewer-app.tsx +++ b/src/pages/viewer-app/viewer-app.tsx @@ -49,6 +49,7 @@ import { LayersPanel } from "../../components/layers-panel/layers-panel"; import { findExampleAndUpdateWithViewState, getActiveLayersByIds, + getLayerUrl, handleSelectAllLeafsInGroup, initActiveLayer, selectNestedLayers, @@ -173,6 +174,7 @@ export const ViewerApp = () => { .map((sublayer) => ({ id: sublayer.id, url: sublayer.url, + fetch: sublayer.fetch, token: sublayer.token, type: sublayer.type ?? TilesetType.I3S, })); @@ -202,7 +204,8 @@ export const ViewerApp = () => { * Hook for start using tilesets stats. */ useEffect(() => { - const tilesetsStats = initStats(activeLayers[0]?.url); + const activeLayerPath = activeLayers[0] ? getLayerUrl(activeLayers[0].url) : undefined; + const tilesetsStats = initStats(activeLayerPath); setTilesetsStats(tilesetsStats); }, [loadedTilesets]); @@ -216,7 +219,13 @@ export const ViewerApp = () => { const tilesetsData: TilesetMetadata[] = []; for (const layer of activeLayers) { - const params = parseTilesetUrlParams(layer.url, layer); + let params = { tilesetUrl: "" as string | File, token: "" }; + if (typeof layer.url === "string") { + params = parseTilesetUrlParams(layer.url, layer); + } else { + params.tilesetUrl = layer.url; + } + const { tilesetUrl, token } = params; tilesetsData.push({ @@ -238,8 +247,9 @@ export const ViewerApp = () => { setSelectedFeatureAttributes(null); setSelectedFeatureIndex(-1); if (buildingExplorerOpened && tilesetsData[0]) { + const tilesetDataUrl = getLayerUrl(tilesetsData[0].url); void dispatch( - getBSLStatisticsSummary({ statSummaryUrl: tilesetsData[0].url }) + getBSLStatisticsSummary({ statSummaryUrl: tilesetDataUrl }) ); } }, [activeLayers, buildingExplorerOpened]); diff --git a/src/redux/slices/flattened-sublayers-slice.spec.ts b/src/redux/slices/flattened-sublayers-slice.spec.ts index 8374e1434..3227102ae 100644 --- a/src/redux/slices/flattened-sublayers-slice.spec.ts +++ b/src/redux/slices/flattened-sublayers-slice.spec.ts @@ -23,13 +23,10 @@ import { wslExpectedLayers, wslTestTileset, } from "./test-data/fluttened-sublayers-slice-test-data"; +import { readFile } from "node:fs/promises"; jest.mock("@loaders.gl/core"); -jest.mock("@loaders.gl/i3s", () => { - return jest.fn().mockImplementation(() => { - return null; - }); -}); +jest.mock("@loaders.gl/i3s"); const previousState: FlattenedSublayersState = { single: { @@ -304,6 +301,36 @@ describe("slice: flattened-sublayers", () => { expect(newSingleState.flattenedSublayers.single.layerCounter).toEqual(1); }); + it("getFlattenedSublayers should call mocked layers loading for SLPK tileset", async () => { + (load as unknown as jest.Mock).mockReturnValue( + Promise.resolve(bslTestTileset) + ); + + const store = setupStore(); + const state = store.getState(); + expect(selectLayers(state)).toEqual([]); + + const slpkBinary = (await readFile("src/redux/slices/test-data/Admin_Building_v18.slpk")).buffer as any; + slpkBinary.size = slpkBinary.byteLength; + + const tilesetsData = [ + { + hasChildren: false, + id: "testSLPK_id", + type: TilesetType.I3S, + url: slpkBinary, + }, + ]; + + await store.dispatch( + getFlattenedSublayers({ tilesetsData, buildingExplorerOpened: false }) + ); + + const newSingleState = store.getState(); + expect(selectSublayers(newSingleState)).toEqual(expectedOverviewSubLayers); + expect(newSingleState.flattenedSublayers.single.layerCounter).toEqual(1); + }); + it("getFlattenedSublayers should call mocked layers loading and put BSL layers and sublayers into the slice state", async () => { (load as unknown as jest.Mock).mockReturnValue( Promise.resolve(bslTestTileset) diff --git a/src/redux/slices/flattened-sublayers-slice.ts b/src/redux/slices/flattened-sublayers-slice.ts index 958730c35..88b0cbebf 100644 --- a/src/redux/slices/flattened-sublayers-slice.ts +++ b/src/redux/slices/flattened-sublayers-slice.ts @@ -6,12 +6,15 @@ import { type TilesetMetadata, type ComparisonSideMode, } from "../../types"; -import { I3SBuildingSceneLayerLoader } from "@loaders.gl/i3s"; +import { I3SBuildingSceneLayerLoader, parseSLPKArchive } from "@loaders.gl/i3s"; import { buildSublayersTree } from "../../utils/sublayers"; -import { load } from "@loaders.gl/core"; +import { type LoaderOptions, load } from "@loaders.gl/core"; import { type RootState } from "../store"; import { type BuildingSceneLayerTileset } from "@loaders.gl/i3s/src/types"; +import { ZipFileSystem } from "@loaders.gl/zip"; +import { FileProvider, BlobFile } from "@loaders.gl/loader-utils"; + // Define a type for the slice states interface SublayersState { /** Array of layers for the currently selected scene */ @@ -144,27 +147,43 @@ const getLayersAndSublayers = async ( | BuildingSceneSublayerExtended[] | Array<{ id: string; - url: string; + url: string | File; visibility: boolean; token?: string; type: TilesetType | undefined; }>; sublayers: Sublayer[]; }> => { + const options: LoaderOptions = {}; + if (typeof tilesetData.url !== "string") { + try { + const fileProvider = await FileProvider.create(new BlobFile(tilesetData.url)); + const archive = await parseSLPKArchive(fileProvider, undefined, tilesetData.url.name); + const fileSystem = new ZipFileSystem(archive); + options.fetch = fileSystem.fetch.bind(fileSystem) as (filename: string) => Promise; + } catch (e) { + console.log(e); + } + } try { const tileset = (await load( - tilesetData.url, - I3SBuildingSceneLayerLoader + typeof tilesetData.url === "string" ? tilesetData.url : "", + I3SBuildingSceneLayerLoader, + options )) as BuildingSceneLayerTileset; const sublayersTree = buildSublayersTree(tileset.header.sublayers); const childSublayers = sublayersTree?.sublayers ?? []; - const overviewLayer = tileset?.sublayers.find( - (sublayer) => sublayer.name === "Overview" - ); + const overviewLayer = { + ...tileset?.sublayers.find( + (sublayer) => sublayer.name === "Overview" + ) as BuildingSceneSublayerExtended, + }; + overviewLayer.fetch = options.fetch as ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); const sublayers = tileset?.sublayers .filter((sublayer) => sublayer.name !== "Overview") .map((item) => ({ ...item, + fetch: options.fetch as ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise), token: tilesetData.token, type: tilesetData.type, })); @@ -182,8 +201,9 @@ const getLayersAndSublayers = async ( layers: [ { id: tilesetData.id, - url: tilesetData.url, + url: typeof tilesetData.url === "string" ? tilesetData.url : tilesetData.id, visibility: true, + fetch: options.fetch, token: tilesetData.token, type: tilesetData.type, }, diff --git a/src/redux/slices/layer-names-slice.ts b/src/redux/slices/layer-names-slice.ts index 5b77e7a63..104a3cdf2 100644 --- a/src/redux/slices/layer-names-slice.ts +++ b/src/redux/slices/layer-names-slice.ts @@ -3,6 +3,7 @@ import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import { type RootState } from "../store"; import { FetchingStatus, type TilesetType } from "../../types"; import { parseTilesetUrlParams } from "../../utils/url-utils"; +import { getLayerUrl } from "../../utils/layer-utils"; // Define a type for the slice state interface LayerNamesState { @@ -27,7 +28,8 @@ const layerNamesSlice = createSlice({ extraReducers: (builder) => { builder .addCase(getLayerNameInfo.pending, (state, action) => { - state.map[action.meta.arg.layerUrl] = { + const layerName = getLayerUrl(action.meta.arg.layerUrl); + state.map[layerName] = { name: "", status: FetchingStatus.pending, }; @@ -39,7 +41,8 @@ const layerNamesSlice = createSlice({ }; }) .addCase(getLayerNameInfo.rejected, (state, action) => { - state.map[action.meta.arg.layerUrl] = { + const layerName = getLayerUrl(action.meta.arg.layerUrl); + state.map[layerName] = { name: "", status: FetchingStatus.ready, }; @@ -49,8 +52,11 @@ const layerNamesSlice = createSlice({ export const getLayerNameInfo = createAsyncThunk< { name: string; layerUrl: string }, -{ layerUrl: string; type: TilesetType; token: string } +{ layerUrl: string | File; type: TilesetType; token: string } >("getLayerNameInfo", async ({ layerUrl, type, token }) => { + if (typeof layerUrl !== "string") { + return { name: "", layerUrl: layerUrl.name }; + } const params = parseTilesetUrlParams(layerUrl, { type, token }); let url = params.tilesetUrl; if (params.token) { diff --git a/src/redux/slices/test-data/Admin_Building_v18.slpk b/src/redux/slices/test-data/Admin_Building_v18.slpk new file mode 100644 index 000000000..9c2b45602 Binary files /dev/null and b/src/redux/slices/test-data/Admin_Building_v18.slpk differ diff --git a/src/types.ts b/src/types.ts index c51c10120..cb42a09ba 100644 --- a/src/types.ts +++ b/src/types.ts @@ -162,7 +162,7 @@ export interface LayerExample { /** Layer's human readable name */ name: string; /** Layer's URL */ - url: string; + url: string | File; /** Layers's authorization token */ token?: string; /** Is layer custom (added by user during application usage) @@ -285,6 +285,8 @@ export interface LoadOptions { token?: string; colorsByAttribute?: ColorsByAttribute | null; }; + fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); + worker?: boolean; } export interface Bookmark { @@ -327,6 +329,7 @@ export interface LayoutProps { export type BuildingSceneSublayerExtended = BuildingSceneSublayer & { token?: string; type?: TilesetType; + fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); }; export enum TileColoredBy { @@ -372,7 +375,7 @@ export interface TileSelectedColor { export interface TilesetMetadata { id: string; - url: string; + url: string | File; token?: string; hasChildren: boolean; type?: TilesetType; diff --git a/src/utils/deckgl/render-layers.spec.ts b/src/utils/deckgl/render-layers.spec.ts index 6d0139a10..0c0cee7c0 100644 --- a/src/utils/deckgl/render-layers.spec.ts +++ b/src/utils/deckgl/render-layers.spec.ts @@ -300,6 +300,7 @@ describe("Render Tile3DLayer", () => { expect(data).toBe(tilesetUrl); expect(loader).toBe(I3SLoader); expect(loadOptions).toEqual({ + worker: true, i3s: { coordinateSystem: COORDINATE_SYSTEM.LNGLAT_OFFSETS, useCompressedTextures: true, @@ -447,6 +448,7 @@ describe("Render Tile3DLayer", () => { useDracoGeometry: true, token: "", }, + worker: true, }); }); diff --git a/src/utils/deckgl/render-layers.ts b/src/utils/deckgl/render-layers.ts index 7170b277f..742649ec2 100644 --- a/src/utils/deckgl/render-layers.ts +++ b/src/utils/deckgl/render-layers.ts @@ -95,6 +95,7 @@ const renderI3SLayer = ( layer: { id?: number | undefined; url?: string | undefined; + fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); token?: string | null | undefined; type: TilesetType; }, @@ -123,9 +124,11 @@ const renderI3SLayer = ( useDracoGeometry, useCompressedTextures, }, + fetch: layer.fetch, + worker: !layer.fetch, }; let url = layer.url; - if (layer.token && url) { + if (layer.token && url && typeof url === "string") { loadOptions.i3s.token = layer.token; const urlObject = new URL(url); urlObject.searchParams.append("token", layer.token); @@ -134,7 +137,6 @@ const renderI3SLayer = ( return new DataDrivenTile3DLayer({ id: `tile-layer-${layer.id}-draco-${useDracoGeometry}-compressed-textures-${useCompressedTextures}--${loadNumber}`, data: url, - // @ts-expect-error loader loader: I3SLoader, colorsByAttribute, customizeColors: colorizeTile, @@ -160,6 +162,7 @@ const renderI3SLayer = ( : layer.url === selectedTilesetBasePath ? selectedIndex : -1, + fetch: layer.fetch, }); }; @@ -295,6 +298,7 @@ export const renderLayers = (params: { layers3d: Array<{ id?: number; url?: string; + fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise); token?: string | null; type: TilesetType; }>; diff --git a/src/utils/layer-utils.ts b/src/utils/layer-utils.ts index 064ed9d4e..3f69b090f 100644 --- a/src/utils/layer-utils.ts +++ b/src/utils/layer-utils.ts @@ -95,7 +95,7 @@ export const findExampleAndUpdateWithViewState = ( for (const example of examplesCopy) { // We can't compare by tileset.url === example.url because BSL and Scene examples url is not loaded as tileset. - if (tileset.url.includes(example.url) && !example.viewState) { + if (tileset.url.includes(getLayerUrl(example.url)) && !example.viewState) { const { zoom, cartographicCenter } = tileset; const [longitude, latitude] = cartographicCenter ?? []; example.viewState = { zoom, latitude, longitude }; @@ -194,3 +194,12 @@ const handleSelectLeafLayer = ( return [...activeLayersInRootGroup, layer]; }; + +/** + * returns string identifier for the layer + * @param layer - layer to define + * @returns layer identifier + */ +export const getLayerUrl = (layer: string | File): string => { + return (!layer || typeof layer === "string") ? layer : layer.name; +}; diff --git a/src/utils/url-utils.ts b/src/utils/url-utils.ts index 0aa29abee..ae4360855 100644 --- a/src/utils/url-utils.ts +++ b/src/utils/url-utils.ts @@ -5,7 +5,7 @@ export const parseTilesetFromUrl = () => { return parsedUrl.searchParams.get("tileset") ?? ""; }; -export const parseTilesetUrlParams = (url, options) => { +export const parseTilesetUrlParams = (url: string, options) => { if (!url) { return { ...options, tilesetUrl: "", token: "", metadataUrl: "" }; } @@ -44,7 +44,7 @@ export const getTilesetType = (url = ""): TilesetType => { } }; -const prepareTilesetUrl = (parsedUrl) => { +const prepareTilesetUrl = (parsedUrl: URL) => { // Try to find particular layer in url. const layer = parsedUrl.pathname.match(/layers\/\d/); diff --git a/yarn.lock b/yarn.lock index 8f69223c7..820107b51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2291,30 +2291,33 @@ "@math.gl/geospatial" "^3.5.1" long "^5.2.1" -"@loaders.gl/3d-tiles@^4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/3d-tiles/-/3d-tiles-4.1.0-alpha.11.tgz#edf0632cb1b8989b41fcf9e8572088d1330866fb" - integrity sha512-8cp+Lh2assMEdh3eIBDwFfr9aNiKpKfemlbeqU5Am29MbKhszOH2+yVy6shMK0/OmQgQ6GvznbMvVU3zriA27g== - dependencies: - "@loaders.gl/draco" "4.1.0-alpha.11" - "@loaders.gl/gltf" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/math" "4.1.0-alpha.11" - "@loaders.gl/tiles" "4.1.0-alpha.11" - "@loaders.gl/zip" "4.1.0-alpha.11" - "@math.gl/core" "^4.0.0" - "@math.gl/geospatial" "^4.0.0" +"@loaders.gl/3d-tiles@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/3d-tiles/-/3d-tiles-4.3.3.tgz#de42c3c4865b806888d36f707d268f4b97dd6912" + integrity sha512-3uOXE8W0ppbY7tI5ywrU3RwCLMZtd+Jh0KgY9+EbjBVnZDHcnFxytYuG4NzfJEf5zwv0jladeSbJS1oVbLi8Jw== + dependencies: + "@loaders.gl/compression" "4.3.3" + "@loaders.gl/crypto" "4.3.3" + "@loaders.gl/draco" "4.3.3" + "@loaders.gl/gltf" "4.3.3" + "@loaders.gl/images" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/math" "4.3.3" + "@loaders.gl/tiles" "4.3.3" + "@loaders.gl/zip" "4.3.3" + "@math.gl/core" "^4.1.0" + "@math.gl/culling" "^4.1.0" + "@math.gl/geospatial" "^4.1.0" "@probe.gl/log" "^4.0.4" long "^5.2.1" -"@loaders.gl/compression@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/compression/-/compression-4.1.0-alpha.11.tgz#72b504b17aa3bc1569519c5c3cf889c12503ceb3" - integrity sha512-IZmUVXFfluGg6NXvIlkuduHLnUD8WvbZZGzsNmRAjlFqTHWlhcLTy9l9CrqlMLWULA3oryWyFSy+2z7IVI3Y0Q== +"@loaders.gl/compression@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/compression/-/compression-4.3.3.tgz#3ec20a13eaf40a8ce93833e7826e2efe642e18d0" + integrity sha512-1IZIFb6MaIiwMwsLEUk5Tyu8qlY7ge2S2Uy2qJxTP23CHakdocue89c54ygo0CgOiUw3Tr1r5JVa3EhB4+lOJQ== dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" "@types/brotli" "^1.3.0" "@types/pako" "^1.0.1" fflate "0.7.4" @@ -2346,24 +2349,23 @@ "@loaders.gl/worker-utils" "4.1.4" "@probe.gl/log" "^4.0.2" -"@loaders.gl/core@^4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-4.1.0-alpha.11.tgz#6c20ff8cb1092ef8edaade26fca2450dfac33c99" - integrity sha512-H2CocuchkAz2eQs8jqUiYlTv6P11o+gveIjZjpgVh/NpwXBORL+cwO1+B3WJ5+WKQdePHgFzL1GPbvM4J04Q6A== +"@loaders.gl/core@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-4.3.3.tgz#be0078d6d47c92219e33813c623bed20f4270ca8" + integrity sha512-RaQ3uNg4ZaVqDRgvJ2CjaOjeeHdKvbKuzFFgbGnflVB9is5bu+h3EKc3Jke7NGVvLBsZ6oIXzkwHijVsMfxv8g== dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" "@probe.gl/log" "^4.0.2" -"@loaders.gl/crypto@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/crypto/-/crypto-4.1.0-alpha.11.tgz#4a5bf79a09b7dfa97ad5a4ae9784c3df1da153da" - integrity sha512-FpUbaeCKei6AfkLZO2nmyQLO6nQO3udk9iZ/icxyNffp2X8sBDEVDu+0Xcm88sfiQ5Ainj41FTotFLxCBZRZ4w== +"@loaders.gl/crypto@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/crypto/-/crypto-4.3.3.tgz#580749a1b89ebaf3e9de17e3eddace1a2c09e562" + integrity sha512-uwqcSGJ4DdS2g3BYc4Noa4EGfnbK63wCQnke4Xyc7KTNl6P70oblDlRbL3df1WQPMTUoXYOERE+ei7Q0Tee4vQ== dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" "@types/crypto-js" "^4.0.2" "@loaders.gl/draco@3.4.14": @@ -2377,16 +2379,15 @@ "@loaders.gl/worker-utils" "3.4.14" draco3d "1.5.5" -"@loaders.gl/draco@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/draco/-/draco-4.1.0-alpha.11.tgz#7785934565a9757ccfb81a7ec6f74e80a1340b9d" - integrity sha512-eoAm8NB0nv9pFus7CJ3miUEJkhwI//PKbU+wAduKjKeNJUSSNxDYY8AJPVKHOw1X2NVlfLhQ8hlfbrj0zZljog== +"@loaders.gl/draco@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/draco/-/draco-4.3.3.tgz#f1dfe19cfcb61e9e70b5fb38cdd39bb914e5602f" + integrity sha512-f2isxvOoH4Pm5p4mGvNN9gVigUwX84j9gdKNMV1aSo56GS1KE3GS2rXaIoy1qaIHMzkPySUTEcOTwayf0hWU7A== dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/schema" "4.1.0-alpha.11" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" - draco3d "1.5.5" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" + draco3d "1.5.7" "@loaders.gl/gis@3.4.14", "@loaders.gl/gis@^3.4.13": version "3.4.14" @@ -2410,33 +2411,36 @@ "@loaders.gl/textures" "3.4.14" "@math.gl/core" "^3.5.1" -"@loaders.gl/gltf@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/gltf/-/gltf-4.1.0-alpha.11.tgz#820bae8be5c17da2be908bca334b8e4d4fb4c4e6" - integrity sha512-f9Z4xKm63fbQNj7Vi1S6vHE2roO+s3u31ifgWhndMgxFt3SYw+mCno2XdfsQuD7TDR1zxIET7sYTbw1shevbxw== - dependencies: - "@loaders.gl/draco" "4.1.0-alpha.11" - "@loaders.gl/images" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/textures" "4.1.0-alpha.11" - "@math.gl/core" "^4.0.0" - -"@loaders.gl/i3s@^4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/i3s/-/i3s-4.1.0-alpha.11.tgz#d0c98b8d63f6583e4d8a246fa5ae32cbb7aef823" - integrity sha512-ydgUfH7MinvNORYYpHTvDK8M+GIKXFP8XU64Xmroetw9ELN8PsmLBPiQB32RXPYay1PfpP8CltKbiTBp5XUxXw== - dependencies: - "@loaders.gl/compression" "4.1.0-alpha.11" - "@loaders.gl/crypto" "4.1.0-alpha.11" - "@loaders.gl/draco" "4.1.0-alpha.11" - "@loaders.gl/images" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/schema" "4.1.0-alpha.11" - "@loaders.gl/textures" "4.1.0-alpha.11" - "@loaders.gl/tiles" "4.1.0-alpha.11" - "@math.gl/core" "^4.0.0" - "@math.gl/culling" "^4.0.0" - "@math.gl/geospatial" "^4.0.0" +"@loaders.gl/gltf@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/gltf/-/gltf-4.3.3.tgz#9a2636927abfb9180725d2e884bcf7296a9ab98a" + integrity sha512-M7jQ7KIB5itctDmGYuT9gndmjNwk1lwQ+BV4l5CoFp38e4xJESPglj2Kj8csWdm3WJhrxIYEP4GpjXK02n8DSQ== + dependencies: + "@loaders.gl/draco" "4.3.3" + "@loaders.gl/images" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/textures" "4.3.3" + "@math.gl/core" "^4.1.0" + +"@loaders.gl/i3s@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/i3s/-/i3s-4.3.3.tgz#e5b21cbbfc408fd6b1bd705a8d5bfad0d869395a" + integrity sha512-un4CoCxKNrTW4nhMTwQxRqPWA9/P2oG6+p5NJjnPPbEcs9QvrHJVXzCnzIpt2nvW7gyZyEHXfwBkVqGrZN48ng== + dependencies: + "@loaders.gl/compression" "4.3.3" + "@loaders.gl/crypto" "4.3.3" + "@loaders.gl/draco" "4.3.3" + "@loaders.gl/images" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/math" "4.3.3" + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/textures" "4.3.3" + "@loaders.gl/tiles" "4.3.3" + "@loaders.gl/zip" "4.3.3" + "@math.gl/core" "^4.1.0" + "@math.gl/culling" "^4.1.0" + "@math.gl/geospatial" "^4.1.0" "@loaders.gl/images@3.4.14", "@loaders.gl/images@^3.4.13": version "3.4.14" @@ -2445,12 +2449,12 @@ dependencies: "@loaders.gl/loader-utils" "3.4.14" -"@loaders.gl/images@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-4.1.0-alpha.11.tgz#79c2bcddd05291ebcb943bb9d47aedaeecc34e91" - integrity sha512-CLXONwuoFXpeWeXbaRbVgZKkZBErrZ1QWX7KHCWnc/Xa4AeXFjMkwhVWh2vh1KYdsVkrz9KBsfn4YHYmg1SWXA== +"@loaders.gl/images@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-4.3.3.tgz#b0ca839bfbd745ca247f1e4c7c34e642a23188b4" + integrity sha512-s4InjIXqEu0T7anZLj4OBUuDBt2BNnAD0GLzSexSkBfQZfpXY0XJNl4mMf5nUKb5NDfXhIKIqv8y324US+I28A== dependencies: - "@loaders.gl/loader-utils" "4.1.0-alpha.11" + "@loaders.gl/loader-utils" "4.3.3" "@loaders.gl/loader-utils@3.4.14", "@loaders.gl/loader-utils@^3.4.13": version "3.4.14" @@ -2461,15 +2465,6 @@ "@loaders.gl/worker-utils" "3.4.14" "@probe.gl/stats" "^4.0.1" -"@loaders.gl/loader-utils@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-4.1.0-alpha.11.tgz#fa2475816334a87bfea5e80d9fbfdc4858e92fce" - integrity sha512-1sDFottMYHKrHqJWPl+hxPFpDn0yG0ykUM6j2tv7oBJNPRuvP3Zbk7EJsJIMCqDpqbt5zabko554ttH2iDo8Sw== - dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" - "@probe.gl/stats" "^4.0.2" - "@loaders.gl/loader-utils@4.1.4", "@loaders.gl/loader-utils@^4.0.0": version "4.1.4" resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-4.1.4.tgz#5bfbdf35db71cae5b541b704cc7d66e66ed8bdad" @@ -2479,6 +2474,16 @@ "@loaders.gl/worker-utils" "4.1.4" "@probe.gl/stats" "^4.0.2" +"@loaders.gl/loader-utils@4.3.3", "@loaders.gl/loader-utils@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-4.3.3.tgz#31621016c972ecc32f71490e92778c288b43a6d8" + integrity sha512-8erUIwWLiIsZX36fFa/seZsfTsWlLk72Sibh/YZJrPAefuVucV4mGGzMBZ96LE2BUfJhadn250eio/59TUFbNw== + dependencies: + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" + "@probe.gl/log" "^4.0.2" + "@probe.gl/stats" "^4.0.2" + "@loaders.gl/math@3.4.14": version "3.4.14" resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-3.4.14.tgz#c27993f0dbe5a88f3ffa07e5240ce27ea5e92392" @@ -2488,14 +2493,14 @@ "@loaders.gl/loader-utils" "3.4.14" "@math.gl/core" "^3.5.1" -"@loaders.gl/math@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-4.1.0-alpha.11.tgz#7ef41e661936d1a362d6de5ce8cd3de5859a44ce" - integrity sha512-VtR9CPdk5nQmqSMEQENXX4LXIZQA9UgO/yHW06xt9qw+4kWu6yafvU3K1Es6OrK5JWrRpelPIalBP9wvEY+bmw== +"@loaders.gl/math@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-4.3.3.tgz#5395521bf97dd028db7a34f8f268b86653139e41" + integrity sha512-oUfCFYsybm6fKnYHU1BzqXsh0sCJ+M9CXNnD/083ZNW+lWdxD44eeTE3DdFYPEMe+yyMkLSGx/8WTMv7ev2t5Q== dependencies: - "@loaders.gl/images" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@math.gl/core" "^4.0.0" + "@loaders.gl/images" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" + "@math.gl/core" "^4.1.0" "@loaders.gl/mvt@^3.4.13": version "3.4.14" @@ -2515,10 +2520,10 @@ dependencies: "@types/geojson" "^7946.0.7" -"@loaders.gl/schema@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-4.1.0-alpha.11.tgz#cf2705ae11e57c99c5784d5b8161decfe17d774e" - integrity sha512-6mRbpmnlBDR9H8fwJU3gsk9XOwOeD290vqoIgioZrrFmSw9vKovtFOpAUf+ISAwWhW0z+yeECjhVbCux8O3nfw== +"@loaders.gl/schema@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-4.3.3.tgz#4466022119e0a7d106a22f8efda1c87789083945" + integrity sha512-zacc9/8je+VbuC6N/QRfiTjRd+BuxsYlddLX1u5/X/cg9s36WZZBlU1oNKUgTYe8eO6+qLyYx77yi+9JbbEehw== dependencies: "@types/geojson" "^7946.0.7" @@ -2545,29 +2550,30 @@ ktx-parse "^0.0.4" texture-compressor "^1.0.2" -"@loaders.gl/textures@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/textures/-/textures-4.1.0-alpha.11.tgz#7984f798b684e6392bd9a6ac1b43d232c24e87f9" - integrity sha512-bds00ZQGIS84c5Z4o9wuoP8bpRhLrSi4h0kU2GqmdHt7KLnQcl9JvylqZV90uaQmTssofC8bX8LLQFavigaaYQ== - dependencies: - "@loaders.gl/images" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/schema" "4.1.0-alpha.11" - "@loaders.gl/worker-utils" "4.1.0-alpha.11" - ktx-parse "^0.0.4" +"@loaders.gl/textures@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/textures/-/textures-4.3.3.tgz#9bd1ea8604a29f80676e46bfa9e2612302263600" + integrity sha512-qIo4ehzZnXFpPKl1BGQG4G3cAhBSczO9mr+H/bT7qFwtSirWVlqsvMlx1Q4VpmouDu+tudwwOlq7B3yqU5P5yQ== + dependencies: + "@loaders.gl/images" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/schema" "4.3.3" + "@loaders.gl/worker-utils" "4.3.3" + "@math.gl/types" "^4.1.0" + ktx-parse "^0.7.0" texture-compressor "^1.0.2" -"@loaders.gl/tiles@3.4.14", "@loaders.gl/tiles@4.1.0-alpha.11", "@loaders.gl/tiles@^3.4.13", "@loaders.gl/tiles@^4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/tiles/-/tiles-4.1.0-alpha.11.tgz#cce23207c327fa02d224cadc829b36c40db56de1" - integrity sha512-rCS4mnsY0CrNhtCij15zxau/P67BFo/B9xy46ZDRlJ1XPC4HDR5LBP33IAAhD/ClqEYP15eeQRxHyQ6HjsD4DQ== - dependencies: - "@loaders.gl/loader-utils" "4.1.0-alpha.11" - "@loaders.gl/math" "4.1.0-alpha.11" - "@math.gl/core" "^4.0.0" - "@math.gl/culling" "^4.0.0" - "@math.gl/geospatial" "^4.0.0" - "@math.gl/web-mercator" "^4.0.0" +"@loaders.gl/tiles@3.4.14", "@loaders.gl/tiles@4.3.3", "@loaders.gl/tiles@^3.4.13", "@loaders.gl/tiles@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/tiles/-/tiles-4.3.3.tgz#7a85bdb0042066164dc38f98a82b5339a0fd8eec" + integrity sha512-cmC/spc+DM5aCSHoHrEuTPhDLuZRtkrWnlHkhC2Tur9uiUr41U3vXnC5slJkOeIWkaN4Q7KRFGCQ6SCendYfMg== + dependencies: + "@loaders.gl/loader-utils" "4.3.3" + "@loaders.gl/math" "4.3.3" + "@math.gl/core" "^4.1.0" + "@math.gl/culling" "^4.1.0" + "@math.gl/geospatial" "^4.1.0" + "@math.gl/web-mercator" "^4.1.0" "@probe.gl/stats" "^4.0.2" "@loaders.gl/wms@^3.4.13": @@ -2591,13 +2597,6 @@ dependencies: "@babel/runtime" "^7.3.1" -"@loaders.gl/worker-utils@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-4.1.0-alpha.11.tgz#48916d93afaf0b0d7a521b4dacb73d9b887ed420" - integrity sha512-GbW+d6L2Shfv4z7Pn9pR61U9y3CVu570JQjiEdzbFS+sQOLZBH+LC5eX+i1MBwtiUDvqjSp0Pm0Ch9bToyYosw== - dependencies: - "@babel/runtime" "^7.3.1" - "@loaders.gl/worker-utils@4.1.4": version "4.1.4" resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-4.1.4.tgz#bc68dfa027cc09a8eb5e5b75e287713f6018586e" @@ -2605,6 +2604,11 @@ dependencies: "@babel/runtime" "^7.3.1" +"@loaders.gl/worker-utils@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-4.3.3.tgz#ab81dd30c7082f35fd69c984a65b46671a43acc7" + integrity sha512-eg45Ux6xqsAfqPUqJkhmbFZh9qfmYuPfA+34VcLtfeXIwAngeP6o4SrTmm9LWLGUKiSh47anCEV1p7borDgvGQ== + "@loaders.gl/xml@3.4.14": version "3.4.14" resolved "https://registry.yarnpkg.com/@loaders.gl/xml/-/xml-3.4.14.tgz#8bfdbed0440cabdc891f152c80b05128cb019d24" @@ -2615,14 +2619,14 @@ "@loaders.gl/schema" "3.4.14" fast-xml-parser "^4.2.5" -"@loaders.gl/zip@4.1.0-alpha.11": - version "4.1.0-alpha.11" - resolved "https://registry.yarnpkg.com/@loaders.gl/zip/-/zip-4.1.0-alpha.11.tgz#cd17ec3c7e1e804eeb075b2af9446707ef740dfc" - integrity sha512-OB9nYgm46hZK9zg1dNx/C0tiiW3+axEIPFUFsS/fjLZ1qy88+9Cv3Z7gVWs22+76rmCJLiFvOy7xc462BuhM7Q== +"@loaders.gl/zip@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@loaders.gl/zip/-/zip-4.3.3.tgz#ed75eaa8b59106b5eafce0f5359938c45ea347dc" + integrity sha512-PPNR9xBLfhBd4Fw69Ai5cUzIJZFCYg3DiYGeR8mA8ik9tuseH+hEBUSsmzU4RFP53xkPLLYvzXjVyiBzfbsjZg== dependencies: - "@loaders.gl/compression" "4.1.0-alpha.11" - "@loaders.gl/crypto" "4.1.0-alpha.11" - "@loaders.gl/loader-utils" "4.1.0-alpha.11" + "@loaders.gl/compression" "4.3.3" + "@loaders.gl/crypto" "4.3.3" + "@loaders.gl/loader-utils" "4.3.3" jszip "^3.1.5" md5 "^2.3.0" @@ -2778,13 +2782,12 @@ "@math.gl/types" "3.6.3" gl-matrix "^3.4.0" -"@math.gl/core@4.0.0", "@math.gl/core@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-4.0.0.tgz#db64af16743ee4def7c1e294b3d1247621d2dabc" - integrity sha512-qGbP4R8G0dsh5OUO+eWKX5NJwZitkV8CdVEolRFSoPteE0lrWxsg01FwAjegKv4jCm975VJ4HxDcb4L6KAiGGw== +"@math.gl/core@4.1.0", "@math.gl/core@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-4.1.0.tgz#2f4a1644c6f8fb50aacae57a02f1297f933aefbd" + integrity sha512-FrdHBCVG3QdrworwrUSzXIaK+/9OCRLscxI2OUy6sLOHyHgBMyfnEGs99/m3KNvs+95BsnQLWklVfpKfQzfwKA== dependencies: - "@babel/runtime" "^7.12.0" - "@math.gl/types" "4.0.0" + "@math.gl/types" "4.1.0" "@math.gl/core@^3.6.0": version "3.6.1" @@ -2804,13 +2807,13 @@ "@math.gl/core" "3.6.3" gl-matrix "^3.4.0" -"@math.gl/culling@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-4.0.0.tgz#5bb96ec38645944088176911a6f4aab43b265cb9" - integrity sha512-8+btkQZtirG64kGbWs/UBoLnXPLw83D1g0sKgZQG32bQLm2dnArMynfqkPs/Mkj5Cm3MvwkTSdz6vN4a3FF6UA== +"@math.gl/culling@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-4.1.0.tgz#efab3137c2964a8a307aa54345481100f0b603ab" + integrity sha512-jFmjFEACnP9kVl8qhZxFNhCyd47qPfSVmSvvjR0/dIL6R9oD5zhR1ub2gN16eKDO/UM7JF9OHKU3EBIfeR7gtg== dependencies: - "@babel/runtime" "^7.12.0" - "@math.gl/core" "4.0.0" + "@math.gl/core" "4.1.0" + "@math.gl/types" "4.1.0" "@math.gl/geospatial@^3.5.1": version "3.5.7" @@ -2821,13 +2824,13 @@ "@math.gl/core" "3.5.7" gl-matrix "~3.3.0" -"@math.gl/geospatial@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-4.0.0.tgz#fdaf8546dc6e2459134a0b2e2bc20a0cc6baac9d" - integrity sha512-bfUqDbu9ZftmiMERMkM1b1N01RVrFUT0d6VuiMRER0d8R5GrWuRccZxROPoS52lyo692nJa0Z4Or97WJxLUYYw== +"@math.gl/geospatial@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-4.1.0.tgz#be7c73842f7744b270a7cdb578edf76859d7d153" + integrity sha512-BzsUhpVvnmleyYF6qdqJIip6FtIzJmnWuPTGhlBuPzh7VBHLonCFSPtQpbkRuoyAlbSyaGXcVt6p6lm9eK2vtg== dependencies: - "@babel/runtime" "^7.12.0" - "@math.gl/core" "4.0.0" + "@math.gl/core" "4.1.0" + "@math.gl/types" "4.1.0" "@math.gl/polygon@^3.5.1": version "3.5.7" @@ -2870,10 +2873,10 @@ resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-3.6.3.tgz#9fa9866feabcbb76de107d78ff3a89c0243ac374" integrity sha512-3uWLVXHY3jQxsXCr/UCNPSc2BG0hNUljhmOBt9l+lNFDp7zHgm0cK2Tw4kj2XfkJy4TgwZTBGwRDQgWEbLbdTA== -"@math.gl/types@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-4.0.0.tgz#20c649dcef8459d9dd1f83a708d7410fe06a3309" - integrity sha512-ZqU7o0LFaWQK/0wYobCwQKrKhRHaihps8oE74CLnWAdTTjXkM2vA8dU7vdx238QfXkNkz4Mv+KYklHpXMQJ8Hw== +"@math.gl/types@4.1.0", "@math.gl/types@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-4.1.0.tgz#ce28c06bcfe07d21311e00aeb25de82fecf7f393" + integrity sha512-clYZdHcmRvMzVK5fjeDkQlHUzXQSNdZ7s4xOqC3nJPgz4C/TZkUecTo9YS4PruZqtDda/ag4erndP0MIn40dGA== "@math.gl/web-mercator@^3.6.2": version "3.6.3" @@ -2883,12 +2886,12 @@ "@babel/runtime" "^7.12.0" gl-matrix "^3.4.0" -"@math.gl/web-mercator@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-4.0.0.tgz#2df70ec478b464337991b9fcdc3acaacf41311cc" - integrity sha512-dtzjaJPckyAEsCT0eHayPoZ8RrHg7XkQq9fZAHAn8CPiyLX0J0ZdvpH1x4a3qe7Ct7CPo6ChnqSk0DwItA4aNQ== +"@math.gl/web-mercator@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-4.1.0.tgz#b244112b2805ba68cdecc76f3d12578d05271a1d" + integrity sha512-HZo3vO5GCMkXJThxRJ5/QYUYRr3XumfT8CzNNCwoJfinxy5NtKUd7dusNTXn7yJ40UoB8FMIwkVwNlqaiRZZAw== dependencies: - "@babel/runtime" "^7.12.0" + "@math.gl/core" "4.1.0" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -5944,6 +5947,11 @@ draco3d@1.5.5: resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.5.tgz#6bf4bbdd65950e6153e991cb0dcb8a10323f610e" integrity sha512-JVuNV0EJzD3LBYhGyIXJLeBID/EVtmFO1ZNhAYflTgiMiAJlbhXQmRRda/azjc8MRVMHh0gqGhiqHUo5dIXM8Q== +draco3d@1.5.7: + version "1.5.7" + resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.7.tgz#94f9bce293eb8920c159dc91a4ce9124a9e899e0" + integrity sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ== + earcut@^2.0.6: version "2.2.3" resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.3.tgz#d44ced2ff5a18859568e327dd9c7d46b16f55cf4" @@ -8483,6 +8491,11 @@ ktx-parse@^0.0.4: resolved "https://registry.yarnpkg.com/ktx-parse/-/ktx-parse-0.0.4.tgz#6fd3eca82490de8a1e48cb8367a9980451fa1ac4" integrity sha512-LY3nrmfXl+wZZdPxgJ3ZmLvG+wkOZZP3/dr4RbQj1Pk3Qwz44esOOSFFVQJcNWpXAtiNIC66WgXufX/SYgYz6A== +ktx-parse@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ktx-parse/-/ktx-parse-0.7.1.tgz#d41514256d7d63acb8ef6ae62dc66f16efc1c39c" + integrity sha512-FeA3g56ksdFNwjXJJsc1CCc7co+AJYDp6ipIp878zZ2bU8kWROatLYf39TQEd4/XRSUvBXovQ8gaVKWPXsCLEQ== + launch-editor@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c"