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

Feature : Upload Custom Labels #276

Merged
merged 1 commit into from
Sep 10, 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
182 changes: 176 additions & 6 deletions frontend/src/components/Layout/TrainingDS/DatasetEditor/AOI.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import axios from "../../../../axios";
import AOIDetails from "./AOIDetails";
import AuthContext from "../../../../Context/AuthContext";
import * as Terraformer from "@terraformer/wkt";

import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
import RefreshIcon from "@mui/icons-material/Refresh";
import UploadProgressModal from "./UploadProgressModal";
const Demo = styled("div")(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
}));
Expand Down Expand Up @@ -60,6 +62,9 @@ const postAoi = async (polygon, dataset, accessToken) => {

const AOI = (props) => {
const [dense, setDense] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [progress, setProgress] = useState({ current: 0, total: 0 });
const [isModalOpen, setIsModalOpen] = useState(false);
const count = Math.ceil(props.mapLayers.length / PER_PAGE);
let [page, setPage] = useState(1);
const [openSnack, setOpenSnack] = useState(false);
Expand All @@ -81,6 +86,134 @@ const AOI = (props) => {
}, [props]);

const { accessToken } = useContext(AuthContext);

const handleFileChange = async (event, aoiId) => {
try {
setIsModalOpen(true);
setIsLoading(true);
const file = event.target.files[0];
if (!file) return;

const headers = {
"access-token": accessToken,
};

const fileContent = await file.text();
const geoJsonData = JSON.parse(fileContent);

if (geoJsonData.type !== "FeatureCollection") {
console.error("Invalid GeoJSON format");
return;
}

const geometries = geoJsonData.features.map(
(feature) => feature.geometry
);
let feature_count = 0;
for (const geometry of geometries) {
feature_count = feature_count + 1;
const progress_updater = {
current: feature_count,
total: geometries.length,
};
setProgress(progress_updater);
console.log(feature_count);
const data = {
geom: JSON.stringify(geometry),
aoi: aoiId,
};

// Send a POST request to the /label/ API for each geometry
const res = await axios.post(`/label/`, data, {
headers,
});

if (res.error) {
console.error(res.error.response.statusText);
} else {
console.log("Label uploaded successfully");
}
}
} catch (e) {
console.error("Error uploading labels", e);
} finally {
setIsLoading(false);
setIsModalOpen(false);
}
};

const downloadAOI = async (aoiId) => {
try {
const headers = {
"access-token": accessToken,
};

const res = await axios.get(`/aoi/${aoiId}/`, {
headers,
responseType: "json",
});

if (res.error) {
console.log(res.error.response.statusText);
} else {
const jsonStr = JSON.stringify(res.data);
console.log(jsonStr);
const blob = new Blob([jsonStr], { type: "application/json" });
const url = window.URL.createObjectURL(blob);

const a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = `AOI_${aoiId}.geojson`;

document.body.appendChild(a);

a.click();

document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
} catch (e) {
console.log("Error:", e);
}
};

const downloadLabels = async (aoiId) => {
try {
const headers = {
"access-token": accessToken,
};

const res = await axios.get(`/label/?aoi=${aoiId}`, {
headers,
responseType: "json",
});

if (res.error) {
console.log(res.error.response.statusText);
} else {
const jsonStr = JSON.stringify(res.data);
console.log(jsonStr);
const blob = new Blob([jsonStr], { type: "application/json" });
const url = window.URL.createObjectURL(blob);

const a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = `AOI_Labels_${aoiId}.geojson`;

document.body.appendChild(a);

a.click();

document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
} catch (e) {
console.log("Error:", e);
}
};

const fetchOSMLebels = async (aoiId) => {
setFetchError(null);
try {
Expand Down Expand Up @@ -110,6 +243,10 @@ const AOI = (props) => {
},
});

const { mutate: mutateDownload, data: fetchdwnld } = useMutation(downloadAOI);
const { mutate: mutateDownloadLables, data: fetchdwnldlabels } =
useMutation(downloadLabels);

const DeleteAOI = async (id, leafletId) => {
try {
const headers = {
Expand Down Expand Up @@ -229,6 +366,7 @@ const AOI = (props) => {
{fileError}
</Alert>
)}
<UploadProgressModal open={isModalOpen} progress={progress} />
<Demo>
{props.mapLayers && props.mapLayers.length > PER_PAGE && (
<Pagination
Expand Down Expand Up @@ -368,6 +506,34 @@ const AOI = (props) => {
}}
>
<MapTwoTone fontSize="small" />
{/* <RefreshIcon fontSize="small" /> */}
</IconButton>
</Tooltip>
<Tooltip title="Download this AOI">
<IconButton
aria-label="comments"
sx={{ width: 24, height: 24 }}
className="margin1"
onClick={(e) => {
mutateDownload(layer.aoiId);
console.log("Downloading AOI as Geojson");
}}
>
<CloudDownloadIcon fontSize="small" />
</IconButton>
</Tooltip>

<Tooltip title="Download Labels in this AOI">
<IconButton
aria-label="comments"
sx={{ width: 24, height: 24 }}
className="margin1"
onClick={(e) => {
mutateDownloadLables(layer.aoiId);
console.log("Downloading AOI Labels as Geojson");
}}
>
<CloudDownloadIcon fontSize="small" />
</IconButton>
</Tooltip>

Expand Down Expand Up @@ -412,16 +578,20 @@ const AOI = (props) => {
<DeleteIcon fontSize="small" />
</IconButton>
</Tooltip>

<input
type="file"
accept=".geojson"
style={{ display: "block" }}
id={`file-input-${layer.aoiId}`}
onChange={(e) => handleFileChange(e, layer.aoiId)}
/>
</ListItemSecondaryAction>
</ListItemWithWiderSecondaryAction>
))}
</List>
</Demo>
{fetchError && (
<Typography variant="body2">
{fetchError}
</Typography>
)}
{fetchError && <Typography variant="body2">{fetchError}</Typography>}
{props.mapLayers && props.mapLayers.length === 0 && (
<Typography variant="body1" component="h2">
No TAs yet, start creating one by clicking Draw a rectangle, 3rd
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { useState } from "react";
import { Dialog, DialogContent, CircularProgress } from "@mui/material";

const UploadProgressModal = ({ open, progress }) => {
console.log(progress);
return (
<Dialog open={open}>
<DialogContent>
<div style={{ textAlign: "center" }}>
<CircularProgress />
<p>
Uploading {progress.current} of {progress.total} labels...
</p>
</div>
</DialogContent>
</Dialog>
);
};

export default UploadProgressModal;
Loading