diff --git a/src/frontend/src/api/CreateProjectService.ts b/src/frontend/src/api/CreateProjectService.ts
index dd3ce11ee4..115348f54f 100755
--- a/src/frontend/src/api/CreateProjectService.ts
+++ b/src/frontend/src/api/CreateProjectService.ts
@@ -7,7 +7,6 @@ import {
OrganisationListModel,
} from '@/models/createproject/createProjectModel';
import { CommonActions } from '@/store/slices/CommonSlice';
-import { ValidateCustomFormResponse } from '@/store/types/ICreateProject';
import { isStatusSuccess } from '@/utilfunctions/commonUtils';
const CreateProjectService = (
@@ -17,6 +16,7 @@ const CreateProjectService = (
formUpload: any,
dataExtractFile: any,
isOsmExtract: boolean,
+ additionalFeature: any,
) => {
return async (dispatch) => {
dispatch(CreateProjectActions.CreateProjectLoading(true));
@@ -74,11 +74,26 @@ const CreateProjectService = (
throw new Error(`Request failed with status ${extractResponse.status}`);
}
+ // post additional feature if available
+ const postAdditionalFeature = await dispatch(
+ PostAdditionalFeatureService(
+ `${import.meta.env.VITE_API_URL}/projects/${projectId}/additional-entity`,
+ additionalFeature,
+ ),
+ );
+
+ hasAPISuccess = postAdditionalFeature;
+ if (!hasAPISuccess) {
+ throw new Error(`Request failed`);
+ }
+
// Generate project files
const generateProjectFile = await dispatch(
GenerateProjectFilesService(
`${import.meta.env.VITE_API_URL}/projects/${projectId}/generate-project-data`,
- projectData,
+ additionalFeature
+ ? { ...projectData, additional_entities: [additionalFeature?.name?.split('.')?.[0]] }
+ : projectData,
formUpload,
),
);
@@ -221,6 +236,39 @@ const GenerateProjectFilesService = (url: string, projectData: any, formUpload:
};
};
+const PostAdditionalFeatureService = (url: string, file: File) => {
+ return async (dispatch) => {
+ const PostAdditionalFeature = async (url, file) => {
+ let isAPISuccess = true;
+
+ try {
+ const additionalFeatureFormData = new FormData();
+ additionalFeatureFormData.append('geojson', file);
+
+ const response = await axios.post(url, additionalFeatureFormData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+
+ isAPISuccess = isStatusSuccess(response.status);
+ } catch (error: any) {
+ isAPISuccess = false;
+ dispatch(
+ CommonActions.SetSnackBar({
+ open: true,
+ message: JSON.stringify(error?.response?.data?.detail),
+ variant: 'error',
+ duration: 2000,
+ }),
+ );
+ }
+ return isAPISuccess;
+ };
+ return await PostAdditionalFeature(url, file);
+ };
+};
+
const OrganisationService = (url: string) => {
return async (dispatch) => {
dispatch(CreateProjectActions.GetOrganisationListLoading(true));
diff --git a/src/frontend/src/components/common/FileInputComponent.tsx b/src/frontend/src/components/common/FileInputComponent.tsx
index 7e6a965175..d06243188b 100644
--- a/src/frontend/src/components/common/FileInputComponent.tsx
+++ b/src/frontend/src/components/common/FileInputComponent.tsx
@@ -57,7 +57,7 @@ const FileInputComponent = ({
{customFile?.name}
)}
- {fileDescription}
+ {fileDescription}
);
};
diff --git a/src/frontend/src/components/createnewproject/DataExtract.tsx b/src/frontend/src/components/createnewproject/DataExtract.tsx
index 9e95fdf5a7..4deab4412a 100644
--- a/src/frontend/src/components/createnewproject/DataExtract.tsx
+++ b/src/frontend/src/components/createnewproject/DataExtract.tsx
@@ -1,6 +1,6 @@
+import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { geojson as fgbGeojson } from 'flatgeobuf';
-import React, { useEffect, useState } from 'react';
import Button from '@/components/common/Button';
import { useDispatch } from 'react-redux';
import { CommonActions } from '@/store/slices/CommonSlice';
@@ -14,16 +14,22 @@ import FileInputComponent from '@/components/common/FileInputComponent';
import DataExtractValidation from '@/components/createnewproject/validation/DataExtractValidation';
import NewDefineAreaMap from '@/views/NewDefineAreaMap';
import useDocumentTitle from '@/utilfunctions/useDocumentTitle';
-import { checkGeomTypeInGeojson } from '@/utilfunctions/checkGeomTypeInGeojson';
import { task_split_type } from '@/types/enums';
import { dataExtractGeojsonType } from '@/store/types/ICreateProject';
+import { CustomCheckbox } from '@/components/common/Checkbox';
const dataExtractOptions = [
{ name: 'data_extract', value: 'osm_data_extract', label: 'Use OSM map features' },
{ name: 'data_extract', value: 'custom_data_extract', label: 'Upload custom map features' },
];
-const DataExtract = ({ flag, customDataExtractUpload, setCustomDataExtractUpload }) => {
+const DataExtract = ({
+ flag,
+ customDataExtractUpload,
+ setCustomDataExtractUpload,
+ additionalFeature,
+ setAdditionalFeature,
+}) => {
useDocumentTitle('Create Project: Map Features');
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -32,6 +38,7 @@ const DataExtract = ({ flag, customDataExtractUpload, setCustomDataExtractUpload
const projectAoiGeojson = useAppSelector((state) => state.createproject.drawnGeojson);
const dataExtractGeojson = useAppSelector((state) => state.createproject.dataExtractGeojson);
const isFgbFetching = useAppSelector((state) => state.createproject.isFgbFetching);
+ const additionalFeatureGeojson = useAppSelector((state) => state.createproject.additionalFeatureGeojson);
const submission = () => {
dispatch(CreateProjectActions.SetIndividualProjectDetailsData(formValues));
@@ -247,7 +254,7 @@ const DataExtract = ({ flag, customDataExtractUpload, setCustomDataExtractUpload
resetFile(setCustomDataExtractUpload);
generateDataExtract();
}}
- className="fmtm-mt-6"
+ className="fmtm-mt-4 !fmtm-mb-8 fmtm-text-base"
isLoading={isFgbFetching}
loadingText="Generating Map Features..."
disabled={dataExtractGeojson && customDataExtractUpload ? true : false}
@@ -272,6 +279,48 @@ const DataExtract = ({ flag, customDataExtractUpload, setCustomDataExtractUpload
/>
>
)}
+ {extractWays && (
+
+ {
+ handleCustomChange('hasAdditionalFeature', status);
+ handleCustomChange('additionalFeature', null);
+ dispatch(CreateProjectActions.SetAdditionalFeatureGeojson(null));
+ setAdditionalFeature(null);
+ }}
+ className="fmtm-text-black"
+ labelClickable
+ />
+ {formValues?.hasAdditionalFeature && (
+ <>
+ {
+ if (e?.target?.files) {
+ const uploadedFile = e?.target?.files[0];
+ setAdditionalFeature(uploadedFile);
+ handleCustomChange('additionalFeature', uploadedFile);
+ const additionalFeatureGeojson = await convertFileToFeatureCol(uploadedFile);
+ dispatch(CreateProjectActions.SetAdditionalFeatureGeojson(additionalFeatureGeojson));
+ }
+ }}
+ onResetFile={() => {
+ resetFile(setAdditionalFeature);
+ dispatch(CreateProjectActions.SetAdditionalFeatureGeojson(null));
+ handleCustomChange('additionalFeature', null);
+ }}
+ customFile={additionalFeature}
+ btnText="Upload Additional Features"
+ accept=".geojson"
+ fileDescription="*The supported file formats are .geojson"
+ errorMsg={errors.additionalFeature}
+ />
+ >
+ )}
+
+ )}
diff --git a/src/frontend/src/components/createnewproject/SelectForm.tsx b/src/frontend/src/components/createnewproject/SelectForm.tsx
index 225b91d265..e3e74faa6a 100644
--- a/src/frontend/src/components/createnewproject/SelectForm.tsx
+++ b/src/frontend/src/components/createnewproject/SelectForm.tsx
@@ -80,7 +80,7 @@ const SelectForm = ({ flag, geojsonFile, customFormFile, setCustomFormFile }) =>
};
useEffect(() => {
if (customFormFile && !customFileValidity) {
- dispatch(ValidateCustomForm(`${import.meta.env.VITE_API_URL}/projects/validate-form`, customFormFile));
+ dispatch(ValidateCustomForm(`${import.meta.env.VITE_API_URL}/projects/validate-form?debug=true`, customFormFile));
}
}, [customFormFile]);
diff --git a/src/frontend/src/components/createnewproject/SplitTasks.tsx b/src/frontend/src/components/createnewproject/SplitTasks.tsx
index df9753f9e1..b1a8f66102 100644
--- a/src/frontend/src/components/createnewproject/SplitTasks.tsx
+++ b/src/frontend/src/components/createnewproject/SplitTasks.tsx
@@ -19,7 +19,7 @@ import { task_split_type } from '@/types/enums';
import useDocumentTitle from '@/utilfunctions/useDocumentTitle';
import { taskSplitOptionsType } from '@/store/types/ICreateProject';
-const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload }) => {
+const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload, additionalFeature }) => {
useDocumentTitle('Create Project: Split Tasks');
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -41,6 +41,7 @@ const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload }) => {
const isTasksGenerated = useAppSelector((state) => state.createproject.isTasksGenerated);
const isFgbFetching = useAppSelector((state) => state.createproject.isFgbFetching);
const toggleSplittedGeojsonEdit = useAppSelector((state) => state.createproject.toggleSplittedGeojsonEdit);
+ const additionalFeatureGeojson = useAppSelector((state) => state.createproject.additionalFeatureGeojson);
const taskSplitOptions: taskSplitOptionsType[] = [
{
@@ -133,6 +134,7 @@ const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload }) => {
projectDetails.customFormUpload,
customDataExtractUpload,
projectDetails.dataExtractWays === 'osm_data_extract',
+ additionalFeature,
),
);
dispatch(CreateProjectActions.SetIndividualProjectDetailsData({ ...projectDetails, ...formValues }));
@@ -373,6 +375,7 @@ const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload }) => {
}
// toggleSplittedGeojsonEdit
hasEditUndo
+ additionalFeatureGeojson={additionalFeatureGeojson}
/>
diff --git a/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx b/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx
index dbe413971e..d35afb6736 100644
--- a/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx
+++ b/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx
@@ -4,6 +4,8 @@ interface ProjectValues {
data_extractFile: object;
data_extract_options: string;
customDataExtractUpload: string;
+ hasAdditionalFeature: boolean;
+ additionalFeature: File;
}
interface ValidationErrors {
form_ways?: string;
@@ -11,6 +13,7 @@ interface ValidationErrors {
data_extractFile?: string;
data_extract_options?: string;
customDataExtractUpload?: string;
+ additionalFeature?: string;
}
function DataExtractValidation(values: ProjectValues) {
@@ -24,6 +27,10 @@ function DataExtractValidation(values: ProjectValues) {
errors.customDataExtractUpload = 'A GeoJSON file is required.';
}
+ if (values.hasAdditionalFeature && !values.additionalFeature) {
+ errors.additionalFeature = 'Additional Feature is Required.';
+ }
+
return errors;
}
diff --git a/src/frontend/src/store/slices/CreateProjectSlice.ts b/src/frontend/src/store/slices/CreateProjectSlice.ts
index d7ebe9fd63..975f304555 100755
--- a/src/frontend/src/store/slices/CreateProjectSlice.ts
+++ b/src/frontend/src/store/slices/CreateProjectSlice.ts
@@ -54,6 +54,7 @@ export const initialState: CreateProjectStateTypes = {
toggleSplittedGeojsonEdit: false,
customFileValidity: false,
validatedCustomForm: null,
+ additionalFeatureGeojson: null,
};
const CreateProject = createSlice({
@@ -225,6 +226,9 @@ const CreateProject = createSlice({
SetValidatedCustomFile(state, action) {
state.validatedCustomForm = action.payload;
},
+ SetAdditionalFeatureGeojson(state, action) {
+ state.additionalFeatureGeojson = action.payload;
+ },
},
});
diff --git a/src/frontend/src/store/types/ICreateProject.ts b/src/frontend/src/store/types/ICreateProject.ts
index dd7c3d6281..b938389fb1 100644
--- a/src/frontend/src/store/types/ICreateProject.ts
+++ b/src/frontend/src/store/types/ICreateProject.ts
@@ -38,6 +38,7 @@ export type CreateProjectStateTypes = {
toggleSplittedGeojsonEdit: boolean;
customFileValidity: boolean;
validatedCustomForm: any;
+ additionalFeatureGeojson: GeoJSONFeatureTypes | null;
};
export type ValidateCustomFormResponse = {
detail: { message: string; possible_reason: string };
@@ -114,6 +115,7 @@ export type ProjectDetailsTypes = {
custom_tms_url: string;
hasCustomTMS: boolean;
customFormUpload: any;
+ hasAdditionalFeature: boolean;
};
export type ProjectAreaTypes = {
diff --git a/src/frontend/src/views/CreateNewProject.tsx b/src/frontend/src/views/CreateNewProject.tsx
index 02e8359894..6594a8d874 100644
--- a/src/frontend/src/views/CreateNewProject.tsx
+++ b/src/frontend/src/views/CreateNewProject.tsx
@@ -24,6 +24,7 @@ const CreateNewProject = () => {
const [geojsonFile, setGeojsonFile] = useState(null);
const [customDataExtractUpload, setCustomDataExtractUpload] = useState(null);
const [customFormFile, setCustomFormFile] = useState(null);
+ const [additionalFeature, setAdditionalFeature] = useState(null);
useEffect(() => {
if (location.pathname !== '/create-project' && !projectDetails.name && !projectDetails.odk_central_url) {
@@ -83,6 +84,8 @@ const CreateNewProject = () => {
flag="create_project"
customDataExtractUpload={customDataExtractUpload}
setCustomDataExtractUpload={setCustomDataExtractUpload}
+ additionalFeature={additionalFeature}
+ setAdditionalFeature={setAdditionalFeature}
/>
);
case '/split-tasks':
@@ -91,6 +94,7 @@ const CreateNewProject = () => {
flag="create_project"
setGeojsonFile={setGeojsonFile}
customDataExtractUpload={customDataExtractUpload}
+ additionalFeature={additionalFeature}
/>
);
default:
diff --git a/src/frontend/src/views/NewDefineAreaMap.tsx b/src/frontend/src/views/NewDefineAreaMap.tsx
index 0d1c81e674..aca5f86059 100644
--- a/src/frontend/src/views/NewDefineAreaMap.tsx
+++ b/src/frontend/src/views/NewDefineAreaMap.tsx
@@ -17,6 +17,7 @@ type NewDefineAreaMapProps = {
onModify?: ((geojson: any, area?: number) => void) | null;
hasEditUndo?: boolean;
getAOIArea?: ((area?: number) => void) | null;
+ additionalFeatureGeojson?: GeoJSONFeatureTypes | null;
};
const NewDefineAreaMap = ({
@@ -29,6 +30,7 @@ const NewDefineAreaMap = ({
onModify,
hasEditUndo,
getAOIArea,
+ additionalFeatureGeojson,
}: NewDefineAreaMapProps) => {
const { mapRef, map }: { mapRef: any; map: any } = useOLMap({
center: [0, 0],
@@ -81,6 +83,18 @@ const NewDefineAreaMap = ({
/>
)}
+ {additionalFeatureGeojson && (
+
+ )}
{buildingExtractedGeojson && (