From 3d281c085d99a27307fc31ee60f49e9f78fcac9b Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Mon, 2 Dec 2024 11:41:53 +0545 Subject: [PATCH 1/5] feat: updated count waypoints within AOI --- src/backend/app/projects/project_logic.py | 31 +++++++++++++ src/backend/app/projects/project_routes.py | 54 +++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/backend/app/projects/project_logic.py b/src/backend/app/projects/project_logic.py index dc88354f..5d5db371 100644 --- a/src/backend/app/projects/project_logic.py +++ b/src/backend/app/projects/project_logic.py @@ -2,6 +2,7 @@ import uuid from loguru import logger as log from fastapi import HTTPException, UploadFile +from pyproj import Transformer from app.tasks.task_splitter import split_by_square from fastapi.concurrency import run_in_threadpool from psycopg import Connection @@ -283,3 +284,33 @@ async def check_regulator_project(db: Connection, project_id: str, email: str): await cur.execute(sql, {"project_id": project_id, "email": email}) project = await cur.fetchone() return bool(project) + + +def generate_square_geojson(center_lat, center_lon, side_length_meters): + transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True) + transformer_back = Transformer.from_crs("EPSG:3857", "EPSG:4326", always_xy=True) + + center_x, center_y = transformer.transform(center_lon, center_lat) + half_side = side_length_meters / 2 + + corners_m = [ + (center_x - half_side, center_y - half_side), + (center_x + half_side, center_y - half_side), + (center_x + half_side, center_y + half_side), + (center_x - half_side, center_y + half_side), + (center_x - half_side, center_y - half_side), + ] + + corners_lat_lon = [transformer_back.transform(x, y) for x, y in corners_m] + + geojson = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Polygon", "coordinates": [corners_lat_lon]}, + } + ], + } + return geojson diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index 1d12141f..88532a49 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -39,7 +39,8 @@ ) from app.users import user_schemas from minio.deleteobjects import DeleteObject - +from app.db.db_models import DbProject +from drone_flightplan import waypoints router = APIRouter( prefix=f"{settings.API_PREFIX}/projects", @@ -657,3 +658,54 @@ async def regulator_approval( status_code=HTTPStatus.BAD_REQUEST, detail=f"An error occurred: {str(e)}", ) + + +@router.get("/{project_id}/waypoints", tags=["Projects"]) +async def get_project_waypoints_geojson( + project_id: uuid.UUID, + side_overlap: float, + front_overlap: float, + altitude_from_ground: float, + gsd_cm_px: float, + meters: float = 100, + is_terrain_follow: bool = False, + project: DbProject = Depends(project_deps.get_project_by_id), + # user_data: AuthUser = Depends(login_required), +): + """ + Get the square GeoJSON for a project's center and count waypoints within AOI. + """ + if project.centroid and project.centroid.coordinates: + center_lon = project.centroid.coordinates.longitude + center_lat = project.centroid.coordinates.latitude + else: + raise Exception( + status_code=HTTPStatus.BAD_REQUEST, + detail="Centroid data is missing or malformed.", + ) + + square_geojson = project_logic.generate_square_geojson( + center_lat, center_lon, meters + ) + + generate_each_points = True if is_terrain_follow else False + generate_3d = ( + False # TODO: For 3d imageries drone_flightplan package needs to be updated. + ) + forward_overlap = front_overlap if front_overlap else 70 + side_overlap = side_overlap if side_overlap else 70 + + points = waypoints.create_waypoint( + project_area=square_geojson, + agl=altitude_from_ground, + gsd=gsd_cm_px, + forward_overlap=forward_overlap, + side_overlap=side_overlap, + rotation_angle=0, + generate_each_points=generate_each_points, + generate_3d=generate_3d, + take_off_point=None, + ) + return { + "avg_no_of_waypoints": len(json.loads(points)["features"]), + } From 73c8f1fdd2477f3a4df38a4aecbfd276468fff13 Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Mon, 2 Dec 2024 11:43:48 +0545 Subject: [PATCH 2/5] fix: added auth in endpoint --- src/backend/app/projects/project_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index 88532a49..cd0aed9b 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -670,7 +670,7 @@ async def get_project_waypoints_geojson( meters: float = 100, is_terrain_follow: bool = False, project: DbProject = Depends(project_deps.get_project_by_id), - # user_data: AuthUser = Depends(login_required), + user_data: AuthUser = Depends(login_required), ): """ Get the square GeoJSON for a project's center and count waypoints within AOI. From 04089f1086124c00acf777cae56c2da37952ce17 Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Mon, 2 Dec 2024 13:49:14 +0545 Subject: [PATCH 3/5] fix: update waypoints count of aoi --- src/backend/app/projects/project_routes.py | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index cd0aed9b..aa9a55c4 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -39,7 +39,6 @@ ) from app.users import user_schemas from minio.deleteobjects import DeleteObject -from app.db.db_models import DbProject from drone_flightplan import waypoints router = APIRouter( @@ -660,34 +659,37 @@ async def regulator_approval( ) -@router.get("/{project_id}/waypoints", tags=["Projects"]) -async def get_project_waypoints_geojson( - project_id: uuid.UUID, +@router.post("/waypoints", tags=["Projects"]) +async def get_project_waypoints_counts( side_overlap: float, front_overlap: float, altitude_from_ground: float, gsd_cm_px: float, meters: float = 100, + project_geojson: UploadFile = File(...), is_terrain_follow: bool = False, - project: DbProject = Depends(project_deps.get_project_by_id), user_data: AuthUser = Depends(login_required), ): """ - Get the square GeoJSON for a project's center and count waypoints within AOI. + Count waypoints within AOI. """ - if project.centroid and project.centroid.coordinates: - center_lon = project.centroid.coordinates.longitude - center_lat = project.centroid.coordinates.latitude - else: - raise Exception( - status_code=HTTPStatus.BAD_REQUEST, - detail="Centroid data is missing or malformed.", - ) + # Validating for .geojson File. + file_name = os.path.splitext(project_geojson.filename) + file_ext = file_name[1] + allowed_extensions = [".geojson", ".json"] + if file_ext not in allowed_extensions: + raise HTTPException(status_code=400, detail="Provide a valid .geojson file") + # read entire file + content = await project_geojson.read() + boundary = geojson.loads(content) + geometry = shape(boundary["features"][0]["geometry"]) + centroid = geometry.centroid + center_lon = centroid.x + center_lat = centroid.y square_geojson = project_logic.generate_square_geojson( center_lat, center_lon, meters ) - generate_each_points = True if is_terrain_follow else False generate_3d = ( False # TODO: For 3d imageries drone_flightplan package needs to be updated. From 4e0affa149151c7b723ca801936cff9a62a25b07 Mon Sep 17 00:00:00 2001 From: Bijay Rauniyar Date: Mon, 2 Dec 2024 15:35:14 +0545 Subject: [PATCH 4/5] feat: add service to get project waypoints --- src/frontend/src/services/createproject.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/src/services/createproject.ts b/src/frontend/src/services/createproject.ts index f3118ed1..e2fee95a 100644 --- a/src/frontend/src/services/createproject.ts +++ b/src/frontend/src/services/createproject.ts @@ -39,3 +39,8 @@ export const regulatorComment = (payload: Record) => { export const getDroneAltitude = (country: string) => authenticated(api).get(`/drones/drone-altitude/${country}/`); + +export const getProjectWayPoints = ( + params: Record, + geojsonData: any, +) => authenticated(api).post(`/projects/waypoints/`, geojsonData, { params }); From 659ea54848960555e1104f58fb1d50cbf773406d Mon Sep 17 00:00:00 2001 From: Bijay Rauniyar Date: Mon, 2 Dec 2024 15:41:29 +0545 Subject: [PATCH 5/5] feat: implementation of project avg taskway points count api --- .../FormContents/GenerateTask/index.tsx | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx b/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx index 316363f2..b8ea000e 100644 --- a/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx +++ b/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ import { useMutation } from '@tanstack/react-query'; import { useState } from 'react'; import ErrorMessage from '@Components/common/ErrorMessage'; @@ -8,7 +9,10 @@ import { toast } from 'react-toastify'; import { setCreateProjectState } from '@Store/actions/createproject'; import { convertGeojsonToFile } from '@Utils/convertLayerUtils'; import prepareFormData from '@Utils/prepareFormData'; -import { postPreviewSplitBySquare } from '@Services/createproject'; +import { + getProjectWayPoints, + postPreviewSplitBySquare, +} from '@Services/createproject'; import MapSection from './MapSection'; export default function GenerateTask({ formProps }: { formProps: any }) { @@ -16,6 +20,14 @@ export default function GenerateTask({ formProps }: { formProps: any }) { const [error, setError] = useState(''); const { register, watch } = formProps; + const { + front_overlap, + side_overlap, + altitude_from_ground, + gsd_cm_px, + outline, + } = watch(); + const dimension = watch('task_split_dimension'); const projectArea = useTypedSelector( @@ -34,6 +46,16 @@ export default function GenerateTask({ formProps }: { formProps: any }) { ...(noFlyZone ? { no_fly_zones: noFlyZoneGeojsonFile } : {}), // add no fly zones it there are any }); + const { mutate: mutateProjectWayPoints, data: projectWayPoints } = + useMutation({ + mutationFn: (projectGeoJsonPayload: Record) => { + const { project_geojson, ...params } = projectGeoJsonPayload; + return getProjectWayPoints(params, { + project_geojson, + }); + }, + }); + const { mutate, isLoading } = useMutation({ mutationFn: postPreviewSplitBySquare, onSuccess: (res: any) => { @@ -82,11 +104,25 @@ export default function GenerateTask({ formProps }: { formProps: any }) { capturedProjectMap: false, }), ); + const projectWayPointsPayload = { + front_overlap: front_overlap || 0, + side_overlap: side_overlap || 0, + altitude_from_ground: altitude_from_ground || 0, + gsd_cm_px: gsd_cm_px || 0, + project_geojson: convertGeojsonToFile(outline), + }; + mutateProjectWayPoints(projectWayPointsPayload); return mutate(payload); }} > Generate Task +

+ The average number of way points is{' '} + + {projectWayPoints?.data?.avg_no_of_waypoints} + +