Skip to content

Commit

Permalink
feat: Show warning if altitude exceeds country's max limit (#370)
Browse files Browse the repository at this point in the history
* feat: add drone altitude regulations model

* feat: Add 'get all' and 'get one' endpoints for drone altitudes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reac: Rename DbDroneAltitude class to DbDroneFlightHeight and update table name

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat: add `OSM_NOMINATIM_URL` on viteconfig and env.example

* feat(project-creation): get project country by project centroid using `OSM Nominatim API`

find centroid with the help of `truf`

Integrated Nominatim API to fetch the country based on the centroid coordinates

* feat: add className optional prop to InfoMessage component for dynamic styling

* feat(create-project): Show warning if altitude exceeds country’s max limit

* feat(individual-project): disable popup only if the user role regulator and has no other roles

* feat(create-project): update fields description

* feat: remove nominatim api from .env and vite config and save as a constant variable since it is constant on all environment

* feat: update .env.example

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Pradip-p <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sujit <[email protected]>
  • Loading branch information
4 people authored Nov 29, 2024
1 parent c99c938 commit 3183a76
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useNavigate } from 'react-router-dom';
import { useTypedSelector, useTypedDispatch } from '@Store/hooks';
import { useMutation } from '@tanstack/react-query';
import { useMutation, useQuery } from '@tanstack/react-query';
import { FieldValues, useForm } from 'react-hook-form';
import {
BasicInformationForm,
Expand All @@ -12,6 +12,7 @@ import {
import { UseFormPropsType } from '@Components/common/FormUI/types';
import { FlexRow } from '@Components/common/Layouts';
import { Button } from '@Components/RadixComponents/Button';
import centroid from '@turf/centroid';
import {
resetUploadedAndDrawnAreas,
setCreateProjectState,
Expand All @@ -26,7 +27,9 @@ import { convertGeojsonToFile } from '@Utils/convertLayerUtils';
import prepareFormData from '@Utils/prepareFormData';
import hasErrorBoundary from '@Utils/hasErrorBoundary';
import { getFrontOverlap, getSideOverlap, gsdToAltitude } from '@Utils/index';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { getCountry } from '@Services/common';
import { setCommonState } from '@Store/actions/common';

/**
* This function looks up the provided map of components to find and return
Expand Down Expand Up @@ -65,6 +68,7 @@ const getActiveStepForm = (activeStep: number, formProps: UseFormPropsType) => {
const CreateprojectLayout = () => {
const dispatch = useTypedDispatch();
const navigate = useNavigate();
const [projectCentroid, setProjectCentroid] = useState<number[] | null>(null);

const activeStep = useTypedSelector(state => state.createproject.activeStep);
const splitGeojson = useTypedSelector(
Expand Down Expand Up @@ -191,6 +195,24 @@ const CreateprojectLayout = () => {
dispatch(setCreateProjectState({ activeStep: activeStep - 1 }));
};

const { isFetching: isFetchingCountry } = useQuery({
queryFn: () =>
getCountry({
lon: projectCentroid?.[0] || 0,
lat: projectCentroid?.[1] || 0,
format: 'json',
}),
queryKey: ['country', projectCentroid?.[0], projectCentroid?.[1]],
enabled: !!projectCentroid,
select(data) {
dispatch(
setCommonState({
projectCountry: data?.data?.address?.country || null,
}),
);
},
});

const onSubmit = (data: any) => {
if (activeStep === 2) {
if (
Expand All @@ -207,6 +229,8 @@ const CreateprojectLayout = () => {
toast.error('Please upload or draw and save No Fly zone area');
return;
}
const newCentroid = centroid(data.outline)?.geometry?.coordinates;
setProjectCentroid(newCentroid);
}

if (activeStep === 3) {
Expand Down Expand Up @@ -297,6 +321,7 @@ const CreateprojectLayout = () => {
splitGeojson: null,
uploadedProjectArea: null,
uploadedNoFlyZone: null,
projectCountry: null,
}),
);
};
Expand Down Expand Up @@ -336,7 +361,12 @@ const CreateprojectLayout = () => {
className="!naxatw-bg-red !naxatw-text-white"
rightIcon="chevron_right"
withLoader
isLoading={isLoading || isCreatingProject || !capturedProjectMap}
isLoading={
isLoading ||
isCreatingProject ||
!capturedProjectMap ||
isFetchingCountry
}
disabled={isLoading || isCreatingProject || !capturedProjectMap}
>
{activeStep === 5 ? 'Save' : 'Next'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import ErrorMessage from '@Components/common/FormUI/ErrorMessage';
import { UseFormPropsType } from '@Components/common/FormUI/types';
import { setCreateProjectState } from '@Store/actions/createproject';
import hasErrorBoundary from '@Utils/hasErrorBoundary';
import { useQuery } from '@tanstack/react-query';
import { getDroneAltitude } from '@Services/createproject';
// import { terrainOptions } from '@Constants/createProject';
import { FlexRow } from '@Components/common/Layouts';
import Switch from '@Components/RadixComponents/Switch';
Expand All @@ -25,6 +27,7 @@ import {
} from '@Utils/index';
import SwitchTab from '@Components/common/SwitchTab';
import { Controller } from 'react-hook-form';

import OutputOptions from './OutputOptions';

const KeyParameters = ({ formProps }: { formProps: UseFormPropsType }) => {
Expand All @@ -51,6 +54,14 @@ const KeyParameters = ({ formProps }: { formProps: UseFormPropsType }) => {
const imageMergeType = useTypedSelector(
state => state.createproject.imageMergeType,
);
const projectCountry = useTypedSelector(state => state.common.projectCountry);

const { data: droneAltitude } = useQuery({
queryKey: ['drone-altitude', projectCountry],
queryFn: () => getDroneAltitude(projectCountry || ''),
select: data => data.data,
enabled: !!projectCountry,
});

// get altitude
const agl =
Expand Down Expand Up @@ -115,7 +126,9 @@ const KeyParameters = ({ formProps }: { formProps: UseFormPropsType }) => {
) : (
<></>
)}
<ErrorMessage message={errors?.gsd_cm_px?.message as string} />
{errors?.gsd_cm_px?.message && (
<ErrorMessage message={errors?.gsd_cm_px?.message as string} />
)}
</FormControl>
) : (
<FormControl className="naxatw-mt-4 naxatw-gap-1">
Expand Down Expand Up @@ -146,11 +159,25 @@ const KeyParameters = ({ formProps }: { formProps: UseFormPropsType }) => {
<></>
)}

<ErrorMessage
message={errors?.altitude_from_ground?.message as string}
/>
{errors?.altitude_from_ground?.message && (
<ErrorMessage
message={errors?.altitude_from_ground?.message as string}
/>
)}
</FormControl>
)}

{droneAltitude?.country &&
droneAltitude?.max_altitude_ft &&
(altitudeInputValue > droneAltitude?.max_altitude_m ||
gsdToAltitude(Number(gsdInputValue)) >
droneAltitude?.max_altitude_m) && (
<InfoMessage
className="naxatw-text-[#FFA500]"
message={`Since your project is in ${droneAltitude?.country}, the maximum allowable drone altitude is ${droneAltitude?.max_altitude_m} m. Please proceed further if you have permission to fly higher.`}
/>
)}

<FormControl className="naxatw-mt-5">
<Label>Merge Type</Label>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ const MapSection = ({ projectData }: { projectData: Record<string, any> }) => {

return (
feature?.source?.includes('tasks-layer') &&
!userDetails?.role?.includes('REGULATOR') // Don't show popup if user role is regulator
!(
(
userDetails?.role?.length === 1 &&
userDetails?.role?.includes('REGULATOR')
) // Don't show popup if user role is regulator any and no other roles
)
);
}}
fetchPopupData={(properties: Record<string, any>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
interface IInfoMessageProp {
message: string;
className?: string;
}

const InfoMessage = ({ message }: IInfoMessageProp) => {
const InfoMessage = ({ message, className }: IInfoMessageProp) => {
return (
<span
role="alert"
className="naxatw-px-1 naxatw-pt-0 naxatw-text-sm naxatw-text-[#17A2B8]"
className={`naxatw-px-1 naxatw-pt-0 naxatw-text-sm naxatw-text-[#17A2B8] ${className}`}
>
{message}
</span>
Expand Down
15 changes: 15 additions & 0 deletions src/frontend/src/constants/createProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ export const contributionsInfo = [
key: 'Instructions for Drone Operators',
description: 'Detailed instructions or parameters for the drone operation.',
},
{
key: 'Deadline for Submission',
description: 'Date for specifying when the project should be submitted.',
},
{
key: 'Does this project require approval from the local regulatory committee?',
description:
'Indicate if the project requires approval from the local regulatory committee before proceeding.',
},
{
key: 'Local regulation committee Email Address',
description:
'The email addresses of local regulatory committee members. If one of them approves or rejects the project, it will be processed accordingly. This is required for areas where drone flights are restricted and require permission',
},

{
key: 'Approval for task lock',
description:
Expand Down
9 changes: 9 additions & 0 deletions src/frontend/src/services/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { UserProfileDetailsType } from '@Components/GoogleAuth/types';
import axios from 'axios';
import { api, authenticated } from '.';

const OSM_NOMINATIM_URL = 'https://nominatim.openstreetmap.org';

export const signInUser = (data: any) => api.post('/users/login/', data);

export const signInGoogle = () => api.get('/users/google-login');
Expand All @@ -27,3 +30,9 @@ export const patchUserProfile = ({ userId, data }: Record<string, any>) =>
authenticated(api).patch(`/users/${userId}/profile`, data, {
headers: { 'Content-Type': 'application/json' },
});

export const getCountry = (params: {
lat: number;
lon: number;
format: string;
}) => axios.get(`${OSM_NOMINATIM_URL}/reverse`, { params });
3 changes: 3 additions & 0 deletions src/frontend/src/services/createproject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ export const regulatorComment = (payload: Record<string, any>) => {
},
);
};

export const getDroneAltitude = (country: string) =>
authenticated(api).get(`/drones/drone-altitude/${country}/`);
2 changes: 2 additions & 0 deletions src/frontend/src/store/slices/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface CommonState {
isCertifiedDroneUser: 'yes' | 'no';
projectSearchKey: string;
selectedDocumentDetails: documentDetailType | null;
projectCountry: string | null;
}

const initialState: CommonState = {
Expand All @@ -35,6 +36,7 @@ const initialState: CommonState = {
isCertifiedDroneUser: 'no',
projectSearchKey: '',
selectedDocumentDetails: null,
projectCountry: null,
};

const setCommonState: CaseReducer<
Expand Down

0 comments on commit 3183a76

Please sign in to comment.