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

feat: slpk from local filesystem feature #377

Merged
merged 8 commits into from
Nov 28, 2024
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
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = {
},
parser: "@typescript-eslint/parser",
extends: ["standard-with-typescript", "plugin:react/recommended"],
// ignorePatterns: ["**/*.html"],
ignorePatterns: ["**/*.slpk"],
overrides: [
{
env: {
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
16 changes: 12 additions & 4 deletions src/components/comparison/comparison-side/comparison-side.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { parseTilesetUrlParams } from "../../../utils/url-utils";
import {
findExampleAndUpdateWithViewState,
getActiveLayersByIds,
getLayerUrl,
handleSelectAllLeafsInGroup,
selectNestedLayers,
} from "../../../utils/layer-utils";
Expand Down Expand Up @@ -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({
Expand All @@ -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]);
Expand All @@ -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
Expand Down Expand Up @@ -317,7 +325,7 @@ export const ComparisonSide = ({
}));
};

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
Expand Down
1 change: 1 addition & 0 deletions src/components/deck-gl-wrapper/deck-gl-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ interface DeckGlI3sProps {
layers3d: Array<{
id?: number;
url?: string;
fetch?: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>);
token?: string | null;
type: TilesetType;
}>;
Expand Down
25 changes: 15 additions & 10 deletions src/components/layers-panel/layers-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { getTilesetType, convertUrlToRestFormat } from "../../utils/url-utils";
import { convertArcGisSlidesToBookmars } from "../../utils/bookmarks-utils";
import { useAppDispatch } from "../../redux/hooks";
import { addBaseMap } from "../../redux/slices/base-maps-slice";
import { getLayerUrl } from "../../utils/layer-utils";

const EXISTING_AREA_ERROR = "You are trying to add an existing area to the map";

Expand Down Expand Up @@ -164,6 +165,8 @@ interface LayersPanelProps {
onBuildingExplorerOpened: (opened: boolean) => void;
}

const getPath = (url: string | File) => getLayerUrl(url);

export const LayersPanel = ({
id,
pageId,
Expand Down Expand Up @@ -208,20 +211,21 @@ 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) {
setShowExistedError(true);
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);
Expand Down Expand Up @@ -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) {
Expand All @@ -264,10 +268,10 @@ export const LayersPanel = ({

// TODO Add loader to show webscene loading
const handleInsertScene = async (scene: CustomLayerData): Promise<void> => {
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) {
Expand All @@ -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),
Expand Down Expand Up @@ -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;
}
Expand All @@ -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",
Expand Down
12 changes: 10 additions & 2 deletions src/pages/debug-app/debug-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { useAppLayout } from "../../utils/hooks/layout";
import {
findExampleAndUpdateWithViewState,
getActiveLayersByIds,
getLayerUrl,
handleSelectAllLeafsInGroup,
initActiveLayer,
selectNestedLayers,
Expand Down Expand Up @@ -218,7 +219,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]);

Expand All @@ -232,7 +234,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({
Expand Down
16 changes: 13 additions & 3 deletions src/pages/viewer-app/viewer-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { LayersPanel } from "../../components/layers-panel/layers-panel";
import {
findExampleAndUpdateWithViewState,
getActiveLayersByIds,
getLayerUrl,
handleSelectAllLeafsInGroup,
initActiveLayer,
selectNestedLayers,
Expand Down Expand Up @@ -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,
}));
Expand Down Expand Up @@ -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]);

Expand All @@ -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;
}
Comment on lines +222 to +227
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this part is different in src/pages/debug-app/debug-app.tsx and src/components/comparison/comparison-side/comparison-side.tsx?


const { tilesetUrl, token } = params;

tilesetsData.push({
Expand All @@ -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]);
Expand Down
37 changes: 32 additions & 5 deletions src/redux/slices/flattened-sublayers-slice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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<any>).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<any>).mockReturnValue(
Promise.resolve(bslTestTileset)
Expand Down
35 changes: 27 additions & 8 deletions src/redux/slices/flattened-sublayers-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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") {
belom88 marked this conversation as resolved.
Show resolved Hide resolved
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<Response>;
} 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<Response>);
const sublayers = tileset?.sublayers
.filter((sublayer) => sublayer.name !== "Overview")
.map((item) => ({
...item,
fetch: options.fetch as ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>),
token: tilesetData.token,
type: tilesetData.type,
}));
Expand Down
Loading
Loading