Skip to content

Commit

Permalink
refactor: remove custom logic for loading offline pmtiles in react fr…
Browse files Browse the repository at this point in the history
…ontend
  • Loading branch information
spwoodcock committed Nov 30, 2024
1 parent 0fd6a15 commit afe61c6
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 165 deletions.
1 change: 1 addition & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"ol": "^8.0.0",
"ol-ext": "^4.0.11",
"ol-layerswitcher": "^4.1.1",
"ol-pmtiles": "^1.0.2",
"pako": "^2.1.0",
"pmtiles": "^3.0.6",
"qrcode-generator": "^1.4.4",
Expand Down
21 changes: 21 additions & 0 deletions src/frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 0 additions & 66 deletions src/frontend/src/api/Files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,69 +55,3 @@ export const GetProjectQrCode = (
}, [projectName, odkToken, osmUser]);
return { qrcode };
};

export async function readFileFromOPFS(filePath: string) {
const opfsRoot = await navigator.storage.getDirectory();
const directories = filePath.split('/');

let currentDirectoryHandle = opfsRoot;

// Iterate dirs and get directoryHandles
for (const directory of directories.slice(0, -1)) {
console.log(`Reading OPFS dir: ${directory}`);
try {
currentDirectoryHandle = await currentDirectoryHandle.getDirectoryHandle(directory);
} catch {
return null; // Directory doesn't exist
}
}

// Get file within final directory handle
try {
const filename: any = directories.pop();
console.log(`Getting OPFS file: ${filename}`);
const fileHandle = await currentDirectoryHandle.getFileHandle(filename);
const fileData = await fileHandle.getFile(); // Read the file
return fileData;
} catch {
return null; // File doesn't exist or error occurred
}
}

export async function writeBinaryToOPFS(filePath: string, data: any) {
console.log(`Starting write to OPFS file: ${filePath}`);

const opfsRoot = await navigator.storage.getDirectory();

// Split the filePath into directories and filename
const directories = filePath.split('/');
const filename: any = directories.pop();

// Start with the root directory handle
let currentDirectoryHandle = opfsRoot;

// Iterate over directories and create nested directories
for (const directory of directories) {
console.log(`Creating OPFS dir: ${directory}`);
try {
currentDirectoryHandle = await currentDirectoryHandle.getDirectoryHandle(directory, { create: true });
} catch (error) {
console.error('Error creating directory:', error);
}
}

// Create the file handle within the last directory
const fileHandle = await currentDirectoryHandle.getFileHandle(filename, { create: true });
const writable = await fileHandle.createWritable();

// Write data to the writable stream
try {
await writable.write(data);
} catch (error) {
console.log(error);
}

// Close the writable stream
await writable.close();
console.log(`Finished write to OPFS file: ${filePath}`);
}
21 changes: 3 additions & 18 deletions src/frontend/src/api/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { ProjectActions } from '@/store/slices/ProjectSlice';
import { CommonActions } from '@/store/slices/CommonSlice';
import CoreModules from '@/shared/CoreModules';
import { task_state, task_event } from '@/types/enums';
import { writeBinaryToOPFS } from '@/api/Files';
import { projectInfoType } from '@/models/project/projectModel';

export const ProjectById = (projectId: string) => {
return async (dispatch) => {
Expand Down Expand Up @@ -169,25 +167,12 @@ export const GenerateProjectTiles = (url: string, projectId: string, data: objec
};
};

export const DownloadTile = (url: string, projectId: string | null) => {
export const DownloadTile = (url: string) => {
return async (dispatch) => {
dispatch(ProjectActions.SetDownloadTileLoading({ loading: true }));

const getDownloadTile = async (url: string, projectId: string | null) => {
const getDownloadTile = async (url: string) => {
try {
if (projectId) {
const response = await CoreModules.axios.get(url, {
responseType: 'arraybuffer',
});
const tileData = response.data;
// Copy to OPFS filesystem for offline use
const filePath = `${projectId}/all.pmtiles`;
await writeBinaryToOPFS(filePath, tileData);
// Set the OPFS file path to project state
dispatch(ProjectActions.SetProjectOpfsBasemapPath(filePath));
return;
}

// Open S3 url directly
window.open(url);

Expand All @@ -198,7 +183,7 @@ export const DownloadTile = (url: string, projectId: string | null) => {
dispatch(ProjectActions.SetDownloadTileLoading({ loading: false }));
}
};
await getDownloadTile(url, projectId);
await getDownloadTile(url);
};
};

Expand Down
4 changes: 2 additions & 2 deletions src/frontend/src/components/GenerateBasemap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const GenerateBasemap = ({ projectInfo }: { projectInfo: Partial<projectInfoType
padding: '16px 32px 24px 32px',
maxWidth: '1000px',
});
const downloadBasemap = (url, toOpfs = false) => {
dispatch(DownloadTile(url, toOpfs ? id : null));
const downloadBasemap = (url) => {
dispatch(DownloadTile(url));
};

const getTilesList = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const LayerCard = ({ layer, changeBaseLayerHandler, active }: layerCardPropType)
);
};

const LayerSwitchMenu = ({ map, pmTileLayerData = null }: { map: any; pmTileLayerData?: any }) => {
const LayerSwitchMenu = ({ map, pmTileLayerUrl = null }: { map: any; pmTileLayerUrl?: any }) => {
const { pathname } = useLocation();
const [baseLayers, setBaseLayers] = useState<string[]>(['OSM', 'Satellite', 'None']);
const [hasPMTile, setHasPMTile] = useState(false);
Expand All @@ -78,10 +78,10 @@ const LayerSwitchMenu = ({ map, pmTileLayerData = null }: { map: any; pmTileLaye
}, [projectInfo, pathname, map]);

useEffect(() => {
if (!pmTileLayerData || baseLayers.includes('PMTile')) return;
if (!pmTileLayerUrl || baseLayers.includes('PMTile')) return;
setHasPMTile(true);
setActiveTileLayer('PMTile');
}, [pmTileLayerData]);
}, [pmTileLayerUrl]);

const changeBaseLayer = (baseLayerTitle: string) => {
const allLayers = map.getLayers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { XYZ } from 'ol/source';
import { useLocation } from 'react-router-dom';
import DataTile from 'ol/source/DataTile.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import { FileSource, PMTiles } from 'pmtiles';
import { PMTilesRasterSource } from 'ol-pmtiles';
import windowDimention from '@/hooks/WindowDimension';
import { useAppSelector } from '@/types/reduxTypes';

Expand Down Expand Up @@ -134,41 +134,20 @@ const monochromeMidNight = (visible = false) =>
}),
});

const pmTileLayer = (pmTileLayerData, visible) => {
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener('load', () => resolve(img));
img.addEventListener('error', () => reject(new Error('load failed')));
img.src = src;
});
}

const pmTiles = new PMTiles(new FileSource(pmTileLayerData));

async function loader(z, x, y) {
const response = await pmTiles.getZxy(z, x, y);
const blob = new Blob([response.data]);
const src = URL.createObjectURL(blob);
const image = await loadImage(src);
URL.revokeObjectURL(src);
return image;
}
return new TileLayer({
const pmTileLayer = (pmTileLayerUrl, visible = true) => {
return new WebGLTile({
title: `PMTile`,
type: 'raster pm tiles',
visible: true,
source: new DataTile({
loader,
wrapX: true,
visible: visible,
source: new PMTilesRasterSource({
url: pmTileLayerUrl,
attributions: ['OpenAerialMap'],
tileSize: [512, 512],
maxZoom: 22,
attributions: 'Tiles © OpenAerialMap',
}),
});
};

const LayerSwitcherControl = ({ map, visible = 'osm', pmTileLayerData = null }) => {
const LayerSwitcherControl = ({ map, visible = 'osm', pmTileLayerUrl = null }) => {
const { windowSize } = windowDimention();
const { pathname } = useLocation();

Expand All @@ -181,7 +160,7 @@ const LayerSwitcherControl = ({ map, visible = 'osm', pmTileLayerData = null })
// mapboxMap(visible),
// mapboxOutdoors(visible),
none(visible),
// pmTileLayer(pmTileLayerData, visible),
// pmTileLayer(pmTileLayerUrl, visible),
],
}),
);
Expand Down Expand Up @@ -246,11 +225,11 @@ const LayerSwitcherControl = ({ map, visible = 'osm', pmTileLayerData = null })
}, [map, visible]);

useEffect(() => {
if (!pmTileLayerData) {
if (!pmTileLayerUrl) {
return;
}

const pmTileBaseLayer = pmTileLayer(pmTileLayerData, visible);
const pmTileBaseLayer = pmTileLayer(pmTileLayerUrl, visible);

const currentLayers = basemapLayers.getLayers();
currentLayers.push(pmTileBaseLayer);
Expand All @@ -259,7 +238,7 @@ const LayerSwitcherControl = ({ map, visible = 'osm', pmTileLayerData = null })
return () => {
basemapLayers.getLayers().remove(pmTileBaseLayer);
};
}, [pmTileLayerData]);
}, [pmTileLayerUrl]);

const location = useLocation();
useEffect(() => {}, [map]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import MapLegends from '@/components/MapLegends';
type mapControlComponentType = {
map: any;
projectName: string;
pmTileLayerData: any;
pmTileLayerUrl: any;
};

const btnList = [
Expand All @@ -41,7 +41,7 @@ const btnList = [
},
];

const MapControlComponent = ({ map, projectName, pmTileLayerData }: mapControlComponentType) => {
const MapControlComponent = ({ map, projectName, pmTileLayerUrl }: mapControlComponentType) => {
const { pathname } = useLocation();
const dispatch = CoreModules.useAppDispatch();
const [toggleCurrentLoc, setToggleCurrentLoc] = useState(false);
Expand Down Expand Up @@ -89,7 +89,7 @@ const MapControlComponent = ({ map, projectName, pmTileLayerData }: mapControlCo
</div>
</Tooltip>
))}
<LayerSwitchMenu map={map} pmTileLayerData={pmTileLayerData} />
<LayerSwitchMenu map={map} pmTileLayerUrl={pmTileLayerUrl} />
{/* download options */}
<div
className={`fmtm-relative ${!pathname.includes('project/') ? 'fmtm-hidden' : 'sm:fmtm-hidden'}`}
Expand Down
4 changes: 0 additions & 4 deletions src/frontend/src/store/slices/ProjectSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const initialState: ProjectStateTypes = {
projectPostCommentsLoading: false,
projectGetCommentsLoading: false,
clearEditorContent: false,
projectOpfsBasemapPath: null,
projectTaskActivity: [],
projectActivityLoading: false,
downloadSubmissionLoading: false,
Expand Down Expand Up @@ -131,9 +130,6 @@ const ProjectSlice = createSlice({
UpdateProjectCommentsList(state, action) {
state.projectCommentsList = [...state.projectCommentsList, action.payload];
},
SetProjectOpfsBasemapPath(state, action) {
state.projectOpfsBasemapPath = action.payload;
},
SetProjectTaskActivity(state, action) {
state.projectTaskActivity = action.payload;
},
Expand Down
1 change: 0 additions & 1 deletion src/frontend/src/store/types/IProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type ProjectStateTypes = {
projectPostCommentsLoading: boolean;
projectGetCommentsLoading: boolean;
clearEditorContent: boolean;
projectOpfsBasemapPath: string | null;
projectTaskActivity: projectTaskActivity[];
projectActivityLoading: boolean;
downloadSubmissionLoading: boolean;
Expand Down
Loading

0 comments on commit afe61c6

Please sign in to comment.